home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0 / stk-3 / blt-for-STk-3.0 / bltDragDrop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-28  |  77.7 KB  |  2,764 lines

  1. /*
  2.  * ------------------------------------------------------------------------
  3.  *  PURPOSE:  drag&drop widget registration facility
  4.  *
  5.  *  Allows widgets to be registered as drag&drop sources and targets
  6.  *  for handling "drag-and-drop" operations between Tcl/Tk applications.
  7.  *
  8.  *  USAGE:
  9.  *    blt_drag&drop source
  10.  *    blt_drag&drop source <pathName>
  11.  *    blt_drag&drop source <pathName> config ?options...?
  12.  *    blt_drag&drop source <pathName> handler ?<dataType> <cmd>...?
  13.  *
  14.  *    blt_drag&drop target
  15.  *    blt_drag&drop target <pathName> handler
  16.  *    blt_drag&drop target <pathName> handler ?<dataType> <cmd>...?
  17.  *    blt_drag&drop target <pathName> handle <dataType>
  18.  *
  19.  *    blt_drag&drop drag <pathName> <x> <y>
  20.  *    blt_drag&drop drop <pathName> <x> <y>
  21.  *
  22.  *    blt_drag&drop errors ?<proc>?
  23.  *    blt_drag&drop active
  24.  *    blt_drag&drop location ?<x> <y>?
  25.  *
  26.  * ------------------------------------------------------------------------
  27.  *  AUTHOR:  Michael J. McLennan       Phone: (215)770-2842
  28.  *           AT&T Bell Laboratories   E-mail: aluxpo!mmc@att.com
  29.  *
  30.  *     RCS:  bltDragDrop.c,v 1.10 1994/04/14 21:11:03 gah Exp
  31.  * ========================================================================
  32.  *                 Copyright (c) 1993-1994  AT&T Bell Laboratories
  33.  * ========================================================================
  34.  * Permission to use, copy, modify, and distribute this software and its
  35.  * documentation for any purpose and without fee is hereby granted,
  36.  * provided that the above copyright notice appear in all copies and that
  37.  * both that the copyright notice and warranty disclaimer appear in
  38.  * supporting documentation, and that the names of AT&T Bell Laboratories
  39.  * any of their entities not be used in advertising or publicity
  40.  * pertaining to distribution of the software without specific, written
  41.  * prior permission.
  42.  * 
  43.  * AT&T disclaims all warranties with regard to this software, including
  44.  * all implied warranties of merchantability and fitness.  In no event
  45.  * shall AT&T be liable for any special, indirect or consequential
  46.  * damages or any damages whatsoever resulting from loss of use, data or
  47.  * profits, whether in an action of contract, negligence or other
  48.  * tortuous action, arising out of or in connection with the use or
  49.  * performance of this software.
  50.  * ========================================================================
  51.  */
  52. #include "blt.h"
  53. #include <X11/Xatom.h>
  54.  
  55. #if (TK_MINOR_VERSION > 0)
  56. #define Cursor Tk_Cursor
  57. #endif
  58.  
  59. #ifndef DRAGDROP_VERSION
  60. #define DRAGDROP_VERSION "2.1"
  61. #endif
  62.  
  63. #define DRAGDROP_COMMAND    "blt_drag&drop"   /* Command name */
  64. #define DRAGDROP_CLASS        "DragDrop"        /* CLASS NAME for token window */
  65. #define DRAGDROP_PROPINFO    "BltDragDropInfo" /* Property name */
  66. #define DRAGDROP_ERRORPROC    "tkerror"          /* Error Proc used to report 
  67.                                * drag&drop background errors */
  68.  
  69. #define DEF_BUTTON_ACTIVE_BG_COLOR  BG2
  70. #define DEF_BUTTON_ACTIVE_BG_MONO   BLACK
  71. #define DEF_BUTTON_ACTIVE_FG_COLOR  BLACK
  72. #define DEF_BUTTON_ACTIVE_FG_MONO   WHITE
  73. #define DEF_BUTTON_BG_COLOR         BG2
  74. #define DEF_BUTTON_BG_MONO          WHITE
  75. #define DEF_TOKEN_OUTLINE_COLOR     BLACK
  76. #define DEF_TOKEN_OUTLINE_MONO      BLACK
  77.  
  78. /*
  79.  *  DRAG&DROP ROOT WINDOW HIERARCHY (cached during "drag" operations)
  80.  */
  81. typedef struct DD_WinRep {
  82.   Window win;            /* X window for this record */
  83.   int initialized;        /* non-zero => rest of info is valid */
  84.   int x0, y0;            /* upper-left corner of window */
  85.   int x1, y1;            /* lower-right corner of window */
  86.   char *ddprop;            /* drag&drop property info */
  87.   char *ddinterp;        /* interp name within ddprop */
  88.   char *ddwin;            /* target window name within ddprop */
  89.   char *ddhandlers;        /* list of handlers within ddprop */
  90.   struct DD_WinRep* parent;    /* window containing this as a child */
  91.   struct DD_WinRep* kids;    /* list of child windows */
  92.   struct DD_WinRep* next;    /* next sibling */
  93. } DD_WinRep;
  94.  
  95. /*
  96.  *  DRAG&DROP REGISTRATION DATA
  97.  */
  98. typedef struct {
  99.   Tcl_Interp *interp;        /* interpreter managing this drag&drop command */
  100.   Tk_Window root;        /* main window for application */
  101.   Tcl_HashTable srcList;    /* list of source widgets */
  102.   Tcl_HashTable trgList;    /* list of target widgets */
  103.   char *errorProc;        /* proc invoked for drag&drop errors */
  104.   int numactive;        /* number of active drag&drop operations */
  105.   int locx, locy;        /* last location point */
  106.   DD_WinRep *pool;        /* pool of available DD_WinRep records */
  107. } DD_RegList;
  108.  
  109. typedef struct {
  110.   DD_RegList *ddlist;        /* parent registration list */
  111.   Tk_Window tkwin;        /* registered window */
  112. } DD_RegEntry;
  113.  
  114. /*
  115.  *  DRAG&DROP SOURCE REGISTRATION RECORD
  116.  */
  117. typedef struct DD_SourceHndl {
  118.   char *dataType;               /* name of data type */
  119.   char *cmd;                    /* command used to send data */
  120.   struct DD_SourceHndl* next;   /* next handler in linked list */
  121. } DD_SourceHndl;
  122.  
  123. typedef struct {
  124.   DD_RegList *ddlist;           /* registration list containing this */
  125.  
  126.   Display *display;             /* drag&drop source window display */
  127.   Tk_Window tkwin;              /* drag&drop source window */
  128.   Atom ddAtom;                  /* X atom referring to "DragDropInfo" */
  129.   int button;                   /* button used to invoke drag for sources */
  130.  
  131.   Tk_Window tokenwin;           /* window representing drag item */
  132.   Tk_Anchor tokenAnchor;        /* position of token win relative to mouse */
  133.   Cursor tokenCursor;           /* cursor used when dragging token */
  134.   Tk_3DBorder tokenOutline;     /* outline around token window */
  135.   Tk_3DBorder tokenBorder;      /* border/background for token window */
  136.   int tokenBorderWidth;         /* border width in pixels */
  137.   XColor *rejectFg;             /* color used to draw rejection fg: (\) */
  138.   XColor *rejectBg;             /* color used to draw rejection bg: (\) */
  139.   Pixmap rejectSt;              /* stipple used to draw rejection: (\) */
  140.   GC rejectFgGC;                /* GC used to draw rejection fg: (\) */
  141.   GC rejectBgGC;                /* GC used to draw rejection bg: (\) */
  142.  
  143.   int pkgcmdInProg;             /* non-zero => executing pkgcmd */
  144.   char *pkgcmd;                 /* cmd executed on start of drag op */
  145.   char *pkgcmdResult;           /* result returned by recent pkgcmd */
  146.   char *sitecmd;                /* cmd executed to update token win */
  147.  
  148.   DD_WinRep *allwins;           /* window info (used during "drag") */
  149.   int selfTarget;               /* non-zero => source can drop onto itself */
  150.   int overTargetWin;            /* non-zero => over target window */
  151.   int tokenx, tokeny;           /* last position of token window */
  152.   Tk_TimerToken hidetoken;      /* token for routine to hide tokenwin */
  153.   Cursor normalCursor;        /* cursor restored after dragging */
  154.  
  155.   char *send;                   /* list of data handler names or "all" */
  156.   DD_SourceHndl *handlers;      /* list of data handlers */
  157. } DD_Source;
  158.  
  159. /*
  160.  *  STACK INFO
  161.  */
  162. typedef struct DD_Stack {
  163.   ClientData *values;        /* values on stack */
  164.   int len;            /* number of values on stack */
  165.   int max;            /* maximum size of stack */
  166.   ClientData space[5];        /* initial space for stack data */
  167. } DD_Stack;
  168.  
  169. /*
  170.  *  CONFIG PARAMETERS
  171.  */
  172. static Tk_ConfigSpec SourceConfigSpecs[] = {
  173.  
  174.   {TK_CONFIG_INT,
  175.      "-button", "buttonBinding", "ButtonBinding",
  176.      "3", Tk_Offset(DD_Source, button),
  177.      0},
  178.  
  179. #ifdef STk_CODE
  180.   {TK_CONFIG_CLOSURE,
  181. #else
  182.   {TK_CONFIG_STRING,
  183. #endif
  184.      "-packagecmd", "packageCommand", "Command",
  185.      NULL, Tk_Offset(DD_Source, pkgcmd),
  186.      TK_CONFIG_NULL_OK},
  187.  
  188.   {TK_CONFIG_COLOR,
  189.      "-rejectbg", "rejectBackground", "Background",
  190.      DEF_BUTTON_BG_COLOR, Tk_Offset(DD_Source, rejectBg),
  191.      TK_CONFIG_COLOR_ONLY},
  192.   {TK_CONFIG_COLOR,
  193.      "-rejectbg", "rejectBackground", "Background",
  194.      "white", Tk_Offset(DD_Source, rejectBg),
  195.      TK_CONFIG_MONO_ONLY},
  196.  
  197.   {TK_CONFIG_COLOR,
  198.      "-rejectfg", "rejectForeground", "Foreground",
  199.      "red", Tk_Offset(DD_Source, rejectFg),
  200.      TK_CONFIG_COLOR_ONLY},
  201.   {TK_CONFIG_COLOR,
  202.      "-rejectfg", "rejectForeground", "Foreground",
  203.      "black", Tk_Offset(DD_Source, rejectFg),
  204.      TK_CONFIG_MONO_ONLY},
  205.  
  206.   {TK_CONFIG_BITMAP,
  207.      "-rejectstipple", "rejectStipple", "Stipple",
  208.      (char*)NULL, Tk_Offset(DD_Source, rejectSt),
  209.      TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
  210.   {TK_CONFIG_BITMAP,
  211.      "-rejectstipple", "rejectStipple", "Stipple",
  212.      "gray50", Tk_Offset(DD_Source, rejectSt),
  213.      TK_CONFIG_MONO_ONLY},
  214.  
  215.   {TK_CONFIG_BOOLEAN,
  216.      "-selftarget", "selfTarget", "SelfTarget",
  217. #ifdef STk_CODE
  218.      "#f", Tk_Offset(DD_Source, selfTarget),
  219. #else
  220.      "no", Tk_Offset(DD_Source, selfTarget),
  221. #endif
  222.      0},
  223.  
  224.   {TK_CONFIG_STRING,
  225.      "-send", "send", "Send",
  226.      "all", Tk_Offset(DD_Source, send),
  227.      TK_CONFIG_NULL_OK},
  228.  
  229. #ifdef STk_CODE
  230.   {TK_CONFIG_CLOSURE,
  231. #else
  232.   {TK_CONFIG_STRING,
  233. #endif
  234.      "-sitecmd", "siteCommand", "Command",
  235.      NULL, Tk_Offset(DD_Source, sitecmd),
  236.      TK_CONFIG_NULL_OK},
  237.  
  238.   {TK_CONFIG_ANCHOR,
  239.      "-tokenanchor", "tokenAnchor", "Anchor",
  240.      "center", Tk_Offset(DD_Source, tokenAnchor),
  241.      0},
  242.  
  243.   {TK_CONFIG_BORDER,
  244.      "-tokenbg", "tokenBackground", "Background",
  245.      DEF_BUTTON_BG_COLOR, Tk_Offset(DD_Source, tokenBorder),
  246.      TK_CONFIG_COLOR_ONLY},
  247.   {TK_CONFIG_BORDER,
  248.      "-tokenbg", "tokenBackground", "Background",
  249.      DEF_BUTTON_BG_MONO, Tk_Offset(DD_Source, tokenBorder),
  250.      TK_CONFIG_MONO_ONLY},
  251.  
  252.   {TK_CONFIG_BORDER,
  253.      "-tokenoutline", "tokenOutline", "Outline",
  254.      DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(DD_Source, tokenOutline),
  255.      TK_CONFIG_COLOR_ONLY},
  256.   {TK_CONFIG_BORDER,
  257.      "-tokenoutline", "tokenOutline", "Outline",
  258.      DEF_TOKEN_OUTLINE_MONO, Tk_Offset(DD_Source, tokenOutline),
  259.      TK_CONFIG_MONO_ONLY},
  260.  
  261.   {TK_CONFIG_PIXELS,
  262.      "-tokenborderwidth", "tokenBorderWidth", "BorderWidth",
  263.      "3", Tk_Offset(DD_Source, tokenBorderWidth),
  264.      0},
  265.  
  266.   {TK_CONFIG_CURSOR,
  267.      "-tokencursor", "tokenCursor", "Cursor",
  268.      "center_ptr", Tk_Offset(DD_Source, tokenCursor),
  269.      TK_CONFIG_NULL_OK},
  270.  
  271.   {TK_CONFIG_END,
  272.      (char*)NULL, (char*)NULL, (char*)NULL,
  273.      (char*)NULL, 0,
  274.      0},
  275. };
  276.  
  277.  
  278. /*
  279.  *  DRAG&DROP TARGET REGISTRATION RECORD
  280.  */
  281. typedef struct DD_TargetHndl {
  282.   char *dataType;               /* Name of data type (malloc-ed) */
  283.   char *command;                /* command to handle data type (malloc-ed) */
  284.   struct DD_TargetHndl* next;   /* next handler in linked list */
  285. } DD_TargetHndl;
  286.  
  287. typedef struct {
  288.   DD_RegList *ddlist;           /* registration list containing this */
  289.  
  290.   Display *display;             /* drag&drop target window display */
  291.   Tk_Window tkwin;              /* drag&drop target window */
  292.   DD_TargetHndl *handlers;      /* list of data handlers */
  293. } DD_Target;
  294.  
  295. /*
  296.  * Each "drag&drop" widget window is tagged with a "DragDropInfo"
  297.  * property in XA_STRING format.  This property identifies the
  298.  * window as a "drag&drop" widget, and contains the following:
  299.  *
  300.  *     "<interp-name>]<drag&drop-path>]<handler-list>"
  301.  *
  302.  * The <drag&drop-path> is the window path name of the drag&drop
  303.  * widget, <interp-name> is the name of the interpreter controlling
  304.  * the widget (useful for the "send" command), and <handler-list>
  305.  * is the list of handler types recognized by the widget.
  306.  *
  307.  * When the user invokes the "drag" operation, a snapshot of the
  308.  * entire window hierarchy is made, and windows carrying a
  309.  * "DragDropInfo" property are identified.  As the token window is
  310.  * dragged around, * this snapshot can be queried to determine when
  311.  * the token is over a valid target window.  When the token is
  312.  * dropped over a valid site, the drop information is sent to the
  313.  * application via the usual "send" command.  If communication fails,
  314.  * the drag&drop facility automatically posts a rejection symbol on
  315.  * the token window.
  316.  */
  317.  
  318. /*
  319.  *  Maximum size property that can be read at one time:
  320.  */
  321. #define MAX_PROP_SIZE 1000
  322.  
  323. /*
  324.  *  FORWARD DECLARATIONS
  325.  */
  326. int Blt_DragDropInit _ANSI_ARGS_((Tcl_Interp* interp));
  327.  
  328. static void DragDrop_Delete _ANSI_ARGS_((ClientData clientData));
  329. static int DragDrop_Cmd _ANSI_ARGS_((ClientData clientData,
  330.                      Tcl_Interp *interp, int argc, char **argv));
  331.  
  332. static DD_Source* GetSourceInfo _ANSI_ARGS_((DD_RegList *ddlist,
  333.                          char *pathname, int *newEntry));
  334. static void DestroySourceInfo _ANSI_ARGS_((DD_RegList *ddlist,
  335.                        char *pathName));
  336. static int ConfigSource _ANSI_ARGS_((Tcl_Interp *interp, DD_Source *dsPtr,
  337.                      int argc, char **argv, int flags));
  338. static char* FindSourceHandler _ANSI_ARGS_((DD_Source* dsPtr, char *dtname));
  339. static void PutSourceHandler _ANSI_ARGS_((DD_Source* dsPtr, char *dtname,
  340.                       char *cmd));
  341. static DD_SourceHndl* CreateSourceHandler _ANSI_ARGS_((char *dtname,
  342.                                char *cmd));
  343. #ifdef STk_CODE
  344. static void DestroySourceHandler _ANSI_ARGS_((char *path, DD_SourceHndl *dsHndl));
  345. #else
  346. static void DestroySourceHandler _ANSI_ARGS_((DD_SourceHndl *dsHndl));
  347. #endif
  348. static void UnregSource _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
  349.  
  350. static DD_Target* GetTargetInfo _ANSI_ARGS_((DD_RegList *ddlist,
  351.                          char *pathname, int *newEntry));
  352. static void DestroyTargetInfo _ANSI_ARGS_((DD_RegList *ddlist,
  353.                        char *pathName));
  354. static char* FindTargetHandler _ANSI_ARGS_((DD_Target* dtPtr, char *dtname));
  355. static void PutTargetHandler _ANSI_ARGS_((DD_Target* dtPtr, char *dtname,
  356.                       char *cmd));
  357. static DD_TargetHndl* CreateTargetHandler _ANSI_ARGS_((char *dtname,
  358.                                char *cmd));
  359. #ifdef STk_CODE
  360. static void DestroyTargetHandler _ANSI_ARGS_((char * path, DD_TargetHndl *dtHndl));
  361. #else
  362. static void DestroyTargetHandler _ANSI_ARGS_((DD_TargetHndl *dtHndl));
  363. #endif
  364. static void UnregTarget _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
  365.  
  366. static void DragDropSend _ANSI_ARGS_((DD_Source *dsPtr));
  367. static char* DragDropSendHndlr _ANSI_ARGS_((DD_Source *dsPtr,
  368.                         char *interpName, char *ddName));
  369.  
  370. static DD_WinRep* GetWinRepInfo _ANSI_ARGS_((DD_Source *dsPtr,
  371.                          DD_RegList *ddlist));
  372. static DD_WinRep* FindTargetWin _ANSI_ARGS_((DD_Source *dsPtr, int x, int y));
  373. static DD_WinRep* WinRepAlloc _ANSI_ARGS_((DD_RegList* ddlist));
  374. static void WinRepRelease _ANSI_ARGS_((DD_WinRep* wr, DD_RegList* ddlist));
  375. static void WinRepInit _ANSI_ARGS_((DD_WinRep* wr, DD_Source *dsPtr));
  376.  
  377. static void AddDDProp _ANSI_ARGS_((DD_Target *dtPtr));
  378. static void DDTokenEventProc _ANSI_ARGS_((ClientData, XEvent*));
  379. static void MoveDDToken _ANSI_ARGS_((DD_Source *dsPtr));
  380. static void UpdateDDToken _ANSI_ARGS_((ClientData clientData));
  381. static void HideDDToken _ANSI_ARGS_((ClientData clientData));
  382. static void RejectDDToken _ANSI_ARGS_((DD_Source* dsPtr));
  383.  
  384. static void StackInit _ANSI_ARGS_((DD_Stack *stack));
  385. static void StackDelete _ANSI_ARGS_((DD_Stack *stack));
  386. static void StackPush _ANSI_ARGS_((ClientData cdata, DD_Stack *stack));
  387. static ClientData StackPop _ANSI_ARGS_((DD_Stack *stack));
  388.  
  389.  
  390. /*
  391.  * ------------------------------------------------------------------------
  392.  *  Blt_DragDropInit()
  393.  *
  394.  *  Adds the drag&drop command to the given interpreter.  Should be
  395.  *  invoked to properly install the command whenever a new interpreter
  396.  *  is created.
  397.  * ------------------------------------------------------------------------
  398.  */
  399. int
  400. Blt_DragDropInit(interp)
  401.      Tcl_Interp *interp;  /* interpreter to be updated */
  402. {
  403.   DD_RegList *ddlist;
  404.   Tk_Window tkwin;
  405.   Tcl_CmdInfo cmdInfo;
  406.  
  407.   /*
  408.    *  See if drag&drop is already installed.
  409.    */
  410.   if (Tcl_GetCommandInfo(interp, DRAGDROP_COMMAND, &cmdInfo))
  411.     {
  412.       Tcl_ResetResult(interp);
  413.       Tcl_AppendResult(interp, "already installed: ", DRAGDROP_COMMAND,
  414.                (char*)NULL);
  415.       return TCL_ERROR;
  416.     }
  417.  
  418.   /*
  419.    *  Make sure that this is a Tk application.
  420.    */
  421.   tkwin = Tk_MainWindow(interp);
  422.   if (tkwin == None)
  423.     {
  424.       Tcl_ResetResult(interp);
  425.       Tcl_AppendResult(interp, "requires Tk facilities: ", DRAGDROP_COMMAND,
  426.                (char*)NULL);
  427.       return TCL_ERROR;
  428.     }
  429.  
  430.   /*
  431.    *  Install drag&drop facilities.
  432.    */
  433.   ddlist = (DD_RegList*)malloc(sizeof(DD_RegList));
  434.   ddlist->interp = interp;
  435.   ddlist->root = tkwin;
  436.   Tcl_InitHashTable(&ddlist->srcList,TCL_STRING_KEYS);
  437.   Tcl_InitHashTable(&ddlist->trgList,TCL_STRING_KEYS);
  438.   ddlist->errorProc = strdup(DRAGDROP_ERRORPROC);
  439.   ddlist->numactive = 0;
  440.   ddlist->locx = ddlist->locy = 0;
  441.   ddlist->pool = NULL;
  442.  
  443.   Tcl_CreateCommand(interp, DRAGDROP_COMMAND, DragDrop_Cmd,
  444.             (ClientData)ddlist, DragDrop_Delete);
  445.  
  446.   Tcl_SetVar2(interp, "blt_versions", DRAGDROP_COMMAND, DRAGDROP_VERSION,
  447.           TCL_GLOBAL_ONLY);
  448.  
  449.   return TCL_OK;
  450. }
  451.  
  452. /*
  453.  * ------------------------------------------------------------------------
  454.  *  DragDrop_Delete()
  455.  *
  456.  *  Invoked when the drag&drop command is removed from an interpreter
  457.  *  to free up allocated memory.
  458.  * ------------------------------------------------------------------------
  459.  */
  460. static void
  461. DragDrop_Delete(cdata)
  462.      ClientData cdata;    /* client data for drag&drop command */
  463. {
  464.   DD_RegList *ddlist = (DD_RegList*)cdata;
  465.   DD_WinRep *wrpool, *wrnext;
  466.  
  467. #ifdef STk_CODE
  468.   /* This code is deleted because, it deletes the two D&D hash tables which
  469.    * are reused when deleting the interpreter. This seems to be due to the order
  470.    * STk use for deletions which is different from Tcl.
  471.    * Not very pretty but effective.
  472.    */ 
  473. #else
  474.   Tcl_DeleteHashTable(&ddlist->srcList);
  475.   Tcl_DeleteHashTable(&ddlist->trgList);
  476.   if (ddlist->errorProc != NULL) {
  477.     free((char*)ddlist->errorProc);
  478.   }
  479.   for (wrpool=ddlist->pool; wrpool; wrpool=wrnext)
  480.     {
  481.       wrnext = wrpool->next;
  482.       free((char*)wrpool);
  483.     }
  484.   free((char*)ddlist);
  485. #endif
  486. }
  487.  
  488. /*
  489.  * ------------------------------------------------------------------------
  490.  *  DragDrop_Cmd()
  491.  *
  492.  *  Invoked by TCL whenever the user issues a drag&drop command.
  493.  *  Handles the following syntax:
  494.  *
  495.  *    blt_drag&drop source
  496.  *    blt_drag&drop source <pathName>
  497.  *    blt_drag&drop source <pathName> config ?options...?
  498.  *    blt_drag&drop source <pathName> handler <dataType> <command> ?...?
  499.  *
  500.  *    blt_drag&drop target
  501.  *    blt_drag&drop target <pathName> handler
  502.  *    blt_drag&drop target <pathName> handler <dataType> <command> ?...?
  503.  *    blt_drag&drop target <pathName> handle <dataType>
  504.  *
  505.  *    blt_drag&drop drag <pathName> <x> <y>
  506.  *    blt_drag&drop drop <pathName> <x> <y>
  507.  *
  508.  *    blt_drag&drop errors ?<proc>?
  509.  *    blt_drag&drop active
  510.  *    blt_drag&drop location ?<x> <y>?
  511.  *
  512.  * ------------------------------------------------------------------------
  513.  */
  514. static int
  515. DragDrop_Cmd(clientData, interp, argc, argv)
  516.      ClientData clientData;   /* main window associated with interp */
  517.      Tcl_Interp *interp;      /* current interpreter */
  518.      int argc;                /* number of arguments */
  519.      char **argv;             /* argument strings */
  520. {
  521.   DD_RegList *ddlist = (DD_RegList*)clientData;
  522.   DD_RegEntry *ddentry;
  523.   register DD_Source *dsPtr;
  524.   register DD_Target *dtPtr;
  525.  
  526.   int status, length, x, y, newEntry;
  527.   char c;
  528.  
  529.   Tk_Window tokenwin;
  530.   XSetWindowAttributes atts;
  531.   char buffer[1024];
  532.  
  533.   if (argc < 2)
  534.     {
  535.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  536.                argv[0], " option ?args?\"",
  537.                (char*)NULL);
  538.       return TCL_ERROR;
  539.     }
  540.   c = argv[1][0];
  541.   length = strlen(argv[1]);
  542.  
  543.   /*
  544.    *  HANDLE:  blt_drag&drop source
  545.    *           blt_drag&drop source <pathName>
  546.    *           blt_drag&drop source <pathName> config ?options...?
  547.    *           blt_drag&drop source <pathName> handler <data> <scmd> ?...?
  548.    */
  549.   if ((c == 's') && strncmp(argv[1], "source", length) == 0)
  550.     {
  551.       /*
  552.        *  HANDLE:  blt_drag&drop source
  553.        */
  554.       if (argc == 2)
  555.     {
  556.       Tcl_HashSearch cursor;
  557.       Tcl_HashEntry *entryPtr;
  558.       char *name;
  559.             
  560.       for (entryPtr = Tcl_FirstHashEntry(&ddlist->srcList, &cursor);
  561.            entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) 
  562.         {
  563.           name = Tcl_GetHashKey(&ddlist->srcList, entryPtr);
  564.           Tcl_AppendElement(interp, name);
  565.         }
  566.       return TCL_OK;
  567.     }
  568.  
  569.       /*
  570.        *  Find or create source info...
  571.        */
  572.       dsPtr = GetSourceInfo(ddlist, argv[2], &newEntry);
  573.       dsPtr->tkwin = Tk_NameToWindow(interp, argv[2], ddlist->root);
  574.       if (!dsPtr->tkwin)
  575.     {
  576.       Tcl_AppendResult(interp, "window does not exist: ", argv[2],
  577.                (char*)NULL);
  578.       DestroySourceInfo(ddlist,argv[2]);
  579.       return TCL_ERROR;
  580.     }
  581.       dsPtr->display = Tk_Display(dsPtr->tkwin);
  582.       dsPtr->ddAtom  = XInternAtom(dsPtr->display, DRAGDROP_PROPINFO, False);
  583.  
  584.       if (newEntry)
  585.     ConfigSource(interp, dsPtr, 0, (char**)NULL, 0);
  586.  
  587.       /*
  588.        *  HANDLE:  blt_drag&drop source <pathName> config ?options...?
  589.        */
  590.       if (argc > 3)
  591.     {
  592.       c = argv[3][0];
  593.       length = strlen(argv[3]);
  594.  
  595.       if ((c == 'c') && strncmp(argv[3], "config", length) == 0)
  596.         {
  597.           if (argc == 4)
  598.         status = Tk_ConfigureInfo(interp, dsPtr->tokenwin,
  599.                       SourceConfigSpecs, (char*)dsPtr, (char*)NULL, 0);
  600.  
  601.           else if (argc == 5)
  602.         status = Tk_ConfigureInfo(interp, dsPtr->tokenwin,
  603.                       SourceConfigSpecs, (char*)dsPtr, argv[4], 0);
  604.  
  605.           else
  606.         status = ConfigSource(interp, dsPtr, argc-4, argv+4,
  607.                       TK_CONFIG_ARGV_ONLY);
  608.         }
  609.  
  610.       /*
  611.        *  HANDLE:  blt_drag&drop source <pathName> handler \
  612.        *             ?<data> <scmd>...?
  613.        */
  614.       else if ((c == 'h') && strncmp(argv[3], "handler", length) == 0)
  615.         {
  616.           if (argc == 4)
  617.         {
  618.           DD_SourceHndl *dsHndl = dsPtr->handlers;
  619.           while (dsHndl)
  620.             {
  621.               Tcl_AppendElement(interp, dsHndl->dataType);
  622.               dsHndl = dsHndl->next;
  623.             }
  624.           return TCL_OK;
  625.         }
  626.  
  627.           /*
  628.            *  Process handler definitions
  629.            */
  630.           status = TCL_OK;
  631.           for (x=4; (x < argc) && (status == TCL_OK); x+=2)
  632.         {
  633.           if (x+1 < argc)
  634.             {
  635.               char *p;
  636.               for (p=argv[x]; *p != '\0'; p++)
  637.             if (*p == ' ')
  638.               {
  639.                 Tcl_AppendResult(interp,
  640.                          "bad source handler name \"",
  641.                          argv[x], "\" (should not contain spaces)",
  642.                          (char*)NULL);
  643.                 return TCL_ERROR;
  644.               }
  645.  
  646.               PutSourceHandler(dsPtr, argv[x], argv[x+1]);
  647.             }
  648.           else
  649.             {
  650.               Tcl_AppendResult(interp,
  651.                        "missing command for source handler: should be \"",
  652.                        argv[x], " command\"");
  653.               return TCL_ERROR;
  654.             }
  655.         }
  656.           return TCL_OK;
  657.         }
  658.       else
  659.         {
  660.           Tcl_AppendResult(interp, "bad option \"", argv[3],
  661.                    "\": must be config or handler",
  662.                    (char*)NULL);
  663.           return TCL_ERROR;
  664.         }
  665.     }
  666.  
  667.       if (newEntry)
  668.     {
  669.       /*
  670.        *  Create the window for the drag&drop token...
  671.        */
  672.       sprintf(buffer, "dd-token%x", (int)dsPtr);
  673.       tokenwin = Tk_CreateWindow(dsPtr->ddlist->interp, dsPtr->tkwin,
  674.                      buffer, "");
  675.  
  676.       if (!tokenwin)
  677.         {
  678.           Tcl_AppendResult(interp, "could not create token window",
  679.                    (char*)NULL);
  680.           DestroySourceInfo(ddlist,argv[2]);
  681.           return TCL_ERROR;
  682.         }
  683.       Tk_SetClass(tokenwin, DRAGDROP_CLASS);
  684.       Tk_CreateEventHandler(tokenwin, ExposureMask|StructureNotifyMask,
  685.                 DDTokenEventProc, (ClientData)dsPtr);
  686.  
  687.       atts.override_redirect = True;
  688.       atts.save_under = True;
  689.       Tk_ChangeWindowAttributes(tokenwin,
  690.                     CWOverrideRedirect|CWSaveUnder, &atts);
  691.  
  692.       Tk_SetInternalBorder(tokenwin, 2*dsPtr->tokenBorderWidth);
  693.       dsPtr->tokenwin = tokenwin;
  694. #ifdef STk_CODE
  695.       /* The transcient window exists but is not associated to a
  696.        * Scheme varaible. This makes problem for command
  697.        * such as (winfo 'children XXX) since, in STk 3.0, they 
  698.        * will yield an error when attempting to eveliuate the name
  699.        * What follow is a dirty kludge, but it works
  700.        */
  701.       Tcl_SetVar(interp, Tk_PathName(tokenwin), Tk_PathName(tokenwin), 
  702.              STk_STRINGIFY);
  703. #endif
  704.  
  705.       if (dsPtr->button > 0)
  706.         {
  707. #ifdef STk_CODE
  708.           sprintf(buffer,"
  709. begin 
  710. (bind %.100s \"<ButtonPress-%d>\"   (lambda (|X| |Y|) (%s 'drag %.100s |X| |Y|)))
  711. (bind %.100s \"<B%d-Motion>\"       (lambda (|X| |Y|) (%s 'drag %.100s |X| |Y|)))
  712. (bind %.100s \"<ButtonRelease-%d>\" (lambda (|X| |Y|) (%s 'drop %.100s |X| |Y|)))",
  713. #else
  714.           sprintf(buffer,
  715.               "bind %.100s <ButtonPress-%d> {%s drag %.100s %%X %%Y}; \
  716.                     bind %.100s <B%d-Motion> {%s drag %.100s %%X %%Y}; \
  717.                     bind %.100s <ButtonRelease-%d> {%s drop %.100s %%X %%Y}",
  718. #endif
  719.               argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  720.               argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  721.               argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2]);
  722.  
  723.           if (Tcl_Eval(interp, buffer) != TCL_OK)
  724.         {
  725.           Tk_DestroyWindow(tokenwin);
  726.           DestroySourceInfo(ddlist,argv[2]);
  727.           return TCL_ERROR;
  728.         }
  729.         }
  730.  
  731.       /*
  732.        *  Arrange for the window to unregister itself when it
  733.        *  is destroyed.
  734.        */
  735.       ddentry = (DD_RegEntry*)malloc(sizeof(DD_RegEntry));
  736.       ddentry->ddlist = ddlist;
  737.       ddentry->tkwin = dsPtr->tkwin;
  738.       Tk_CreateEventHandler(dsPtr->tkwin, StructureNotifyMask,
  739.                 UnregSource, (ClientData)ddentry);
  740.     }
  741.     }
  742.  
  743.   /*
  744.    *  HANDLE:  blt_drag&drop target ?<pathName>? ?handling info...?
  745.    */
  746.   else if ((c == 't') && strncmp(argv[1], "target", length) == 0)
  747.     {
  748.       /*
  749.        *  HANDLE:  blt_drag&drop target
  750.        */
  751.       if (argc == 2)
  752.     {
  753.       Tcl_HashSearch pos;
  754.       Tcl_HashEntry *entry = Tcl_FirstHashEntry(&ddlist->trgList,&pos);
  755.       while (entry)
  756.         {
  757.           Tcl_AppendElement(interp,
  758.                 Tcl_GetHashKey(&ddlist->trgList,entry));
  759.           entry = Tcl_NextHashEntry(&pos);
  760.         }
  761.       return TCL_OK;
  762.     }
  763.  
  764.       dtPtr = GetTargetInfo(ddlist,argv[2],&newEntry);
  765.       dtPtr->tkwin = Tk_NameToWindow(interp, argv[2], ddlist->root);
  766.  
  767.       if (!dtPtr->tkwin)
  768.     {
  769.       Tcl_AppendResult(interp, "window does not exist: ", argv[2],
  770.                (char*)NULL);
  771.       DestroyTargetInfo(ddlist,argv[2]);
  772.       return TCL_ERROR;
  773.     }
  774.       dtPtr->display = Tk_Display(dtPtr->tkwin);
  775.  
  776.       /*
  777.        *  If this is a new target, attach a property to identify
  778.        *  window as "drag&drop" target, and arrange for the window
  779.        *  to un-register itself when it is destroyed.
  780.        */
  781.       if (newEntry)
  782.     {
  783.       Tk_MakeWindowExist(dtPtr->tkwin);
  784.       AddDDProp(dtPtr);
  785.  
  786.       /*
  787.        *  Arrange for the window to unregister itself when it
  788.        *  is destroyed.
  789.        */
  790.       ddentry = (DD_RegEntry*)malloc(sizeof(DD_RegEntry));
  791.       ddentry->ddlist = ddlist;
  792.       ddentry->tkwin = dtPtr->tkwin;
  793.       Tk_CreateEventHandler(dtPtr->tkwin, StructureNotifyMask,
  794.                 UnregTarget, (ClientData)ddentry);
  795.     }
  796.  
  797.       /*
  798.        *  HANDLE:  blt_drag&drop target <pathName> handler
  799.        *           blt_drag&drop target <pathName> handler <data> <cmd> ?...?
  800.        */
  801.       if ((argc >= 4) && (strcmp(argv[3], "handler") == 0))
  802.     {
  803.       if (argc == 4)
  804.         {
  805.           DD_TargetHndl *dtHndl = dtPtr->handlers;
  806.           while (dtHndl)
  807.         {
  808.           Tcl_AppendElement(interp, dtHndl->dataType);
  809.           dtHndl = dtHndl->next;
  810.         }
  811.           return TCL_OK;
  812.         }
  813.  
  814.       /*
  815.        *  Process handler definitions
  816.        */
  817.       status = TCL_OK;
  818.       for (x=4; (x < argc) && (status == TCL_OK); x+=2)
  819.         {
  820.           if (x+1 < argc)
  821.         PutTargetHandler(dtPtr, argv[x], argv[x+1]);
  822.           else
  823.         {
  824.           Tcl_AppendResult(interp,
  825.                    "missing command for target handler: should be \"",
  826.                    argv[x], " command\"",
  827.                    (char*)NULL);
  828.           return TCL_ERROR;
  829.         }
  830.         }
  831.       return TCL_OK;
  832.     }
  833.  
  834.       /*
  835.        *  HANDLE:  blt_drag&drop target <pathName> handle <data>
  836.        */
  837.       else if ((argc == 5) && (strcmp(argv[3], "handle") == 0))
  838.     {
  839.       char *cmd;
  840.       if ((cmd=FindTargetHandler(dtPtr, argv[4])) != NULL)
  841.         return Tcl_Eval(interp, cmd);
  842.  
  843.       Tcl_AppendResult(interp, "target cannot handle datatype: ",
  844.                argv[4], (char*)NULL);
  845.       return TCL_ERROR;    /* no handler found */
  846.     }
  847.       else
  848.     {
  849.       Tcl_AppendResult(interp,"usage: ", argv[0], " target ", argv[2],
  850.                " handler ?defns?\n   or: ", argv[0], " target ", argv[2],
  851.                " handle <data>",
  852.                (char*)NULL);
  853.       return TCL_ERROR;
  854.     }
  855.     }
  856.  
  857.   /*
  858.    *  HANDLE:  blt_drag&drop drag <path> <x> <y>
  859.    */
  860.   else if ((c == 'd') && strncmp(argv[1], "drag", length) == 0)
  861.     {
  862.       if (argc < 5)
  863.     {
  864.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  865.                argv[0], " drag pathname x y\"",
  866.                (char*)NULL);
  867.       return TCL_ERROR;
  868.     }
  869.  
  870.       dsPtr = GetSourceInfo(ddlist,argv[2],&newEntry);
  871.       if (newEntry)
  872.     {
  873.       Tcl_AppendResult(interp, "not a drag&drop source: ", argv[2],
  874.                (char*)NULL);
  875.       DestroySourceInfo(ddlist,argv[2]);
  876.       return TCL_ERROR;
  877.     }
  878.       if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
  879.       (Tcl_GetInt(interp, argv[4], &y) != TCL_OK))
  880.     return TCL_ERROR;
  881.  
  882.       ddlist->locx = x;        /* save drag&drop location */
  883.       ddlist->locy = y;
  884.       dsPtr->tokenx = x;
  885.       dsPtr->tokeny = y;
  886.  
  887.       /*
  888.        *  If HideDDToken() is pending, then do it now!
  889.        */
  890.       if (dsPtr->hidetoken)
  891.     {
  892.       Tk_DeleteTimerHandler(dsPtr->hidetoken);
  893.       HideDDToken((ClientData)dsPtr);
  894.     }
  895.  
  896.       /*
  897.        *  If pkgcmd is in progress, then ignore subsequent calls
  898.        *  until it completes.  Only perform drag if pkgcmd
  899.        *  completed successfully and token window is mapped.
  900.        */
  901.       if (!Tk_IsMapped(dsPtr->tokenwin) && !dsPtr->pkgcmdInProg)
  902.     {
  903.       /*
  904.        *  No list of send handlers?  Then source is disabled.
  905.        *  Abort drag quietly.
  906.        */
  907.       if (dsPtr->send == NULL)
  908.         return TCL_OK;
  909.  
  910.       /*
  911.        *  No token command?  Then cannot build token.
  912.        *  Signal error.
  913.        */
  914.       if (!dsPtr->pkgcmd)
  915.         {
  916.           Tcl_AppendResult(interp, "missing -packagecmd: ", argv[2],
  917.                    (char*)NULL);
  918.           return TCL_ERROR;
  919.         }
  920.  
  921.       /*
  922.        *  Execute token command to initialize token window.
  923.        */
  924.       dsPtr->pkgcmdInProg = ~0;
  925.       status = Tcl_VarEval(dsPtr->ddlist->interp,
  926. #ifdef STk_CODE
  927.                   "(", dsPtr->pkgcmd, " '",
  928.                    Tk_PathName(dsPtr->tokenwin), ")",
  929. #else
  930.  
  931.                    dsPtr->pkgcmd, " ", Tk_PathName(dsPtr->tokenwin),
  932.  
  933. #endif
  934.                    (char*)NULL);
  935.       dsPtr->pkgcmdInProg = 0;
  936.  
  937.       /*
  938.        *  Null string from the package command?
  939.        *  Then quietly abort the drag&drop operation.
  940.        */
  941.       if (*interp->result == '\0')
  942.         return TCL_OK;
  943.  
  944.       /*
  945.        *  Save result of token command for send command.
  946.        */
  947.       if (dsPtr->pkgcmdResult)
  948.         free(dsPtr->pkgcmdResult);
  949.       dsPtr->pkgcmdResult = strdup(interp->result);
  950.  
  951.       /*
  952.        *  Token building failed?  If an error handler is defined,
  953.        *  then signal the error.  Otherwise, abort quietly.
  954.        */
  955.       if (status != TCL_OK)
  956.         {
  957.           if (ddlist->errorProc && *ddlist->errorProc)
  958.         {
  959.           return Tcl_VarEval(ddlist->interp,
  960. #ifdef STk_CODE
  961.                      "(", ddlist->errorProc, " ", 
  962.                      STk_stringify(ddlist->interp->result, 0), ")",
  963. #else
  964.                      ddlist->errorProc, " {", ddlist->interp->result, "}",
  965. #endif
  966.                      (char*)NULL);
  967.         }
  968.           else
  969.         return TCL_OK;
  970.         }
  971.  
  972.       /*
  973.        *  Install token cursor...
  974.        */
  975.       if (dsPtr->tokenCursor != None)
  976.         {
  977.           status = Tcl_VarEval(dsPtr->ddlist->interp,
  978. #ifdef STk_CODE
  979.                    "(", Tk_PathName(dsPtr->tkwin), " 'cget :cursor)",
  980. #else
  981.                    Tk_PathName(dsPtr->tkwin), " config -cursor",
  982. #endif
  983.                    (char*)NULL);
  984.  
  985.           if (status == TCL_OK)
  986.         {
  987.           char *cname = interp->result;
  988.           while (*cname != '\0')
  989.             cname++;
  990.  
  991.           while ((cname > interp->result) && (*(cname-1) != ' '))
  992.             cname--;
  993.  
  994.           if (dsPtr->normalCursor != None)
  995.             {
  996.               Tk_FreeCursor(dsPtr->display, dsPtr->normalCursor);
  997.               dsPtr->normalCursor = None;
  998.             }
  999.  
  1000.           if (strcmp(cname,"{}") != 0)
  1001.             dsPtr->normalCursor = Tk_GetCursor(interp,
  1002.                                dsPtr->tkwin, Tk_GetUid(cname));
  1003.         }
  1004.           Tk_DefineCursor(dsPtr->tkwin, dsPtr->tokenCursor);
  1005.         }
  1006.       /*
  1007.        *  Get ready to drag token window...
  1008.        *  1) Cache info for all windows on root
  1009.        *  2) Map token window to begin drag operation
  1010.        */
  1011.       if (dsPtr->allwins)
  1012.         WinRepRelease(dsPtr->allwins, ddlist);
  1013.       dsPtr->allwins = GetWinRepInfo(dsPtr, ddlist);
  1014.  
  1015.       ddlist->numactive++;    /* one more drag&drop window active */
  1016.       Tk_MapWindow(dsPtr->tokenwin);
  1017.       XRaiseWindow(Tk_Display(dsPtr->tokenwin),
  1018.                Tk_WindowId(dsPtr->tokenwin));
  1019.     }
  1020.  
  1021.       /*
  1022.        *  Arrange to update status of token window...
  1023.        */
  1024.       Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1025.       Tk_DoWhenIdle(UpdateDDToken, (ClientData)dsPtr);
  1026.  
  1027.       /*
  1028.        *  Move the token window to the current drag point...
  1029.        */
  1030.       MoveDDToken(dsPtr);
  1031.     }
  1032.  
  1033.   /*
  1034.    *  HANDLE:  blt_drag&drop drop <path> <x> <y>
  1035.    */
  1036.   else if ((c == 'd') && strncmp(argv[1], "drop", length) == 0)
  1037.     {
  1038.       if (argc < 5)
  1039.     {
  1040.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  1041.                argv[0], " drop pathname x y\"",
  1042.                (char*)NULL);
  1043.       return TCL_ERROR;
  1044.     }
  1045.  
  1046.       dsPtr = GetSourceInfo(ddlist,argv[2],&newEntry);
  1047.       if (newEntry)
  1048.     {
  1049.       Tcl_AppendResult(interp, "not a drag&drop source: ", argv[2],
  1050.                (char*)NULL);
  1051.       DestroySourceInfo(ddlist,argv[2]);
  1052.       return TCL_ERROR;
  1053.     }
  1054.       if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
  1055.       (Tcl_GetInt(interp, argv[4], &y) != TCL_OK))
  1056.     return TCL_ERROR;
  1057.  
  1058.       ddlist->locx = x;        /* save drag&drop location */
  1059.       ddlist->locy = y;
  1060.       dsPtr->tokenx = x;
  1061.       dsPtr->tokeny = y;
  1062.  
  1063.       /*
  1064.        *  Put the cursor back to its usual state.
  1065.        */
  1066.       if (dsPtr->normalCursor == None)
  1067.     Tk_UndefineCursor(dsPtr->tkwin);
  1068.       else
  1069.     Tk_DefineCursor(dsPtr->tkwin, dsPtr->normalCursor);
  1070.  
  1071.       Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1072.  
  1073.       /*
  1074.        *  Make sure that token window was not dropped before it
  1075.        *  was either mapped or packed with info.
  1076.        */
  1077.       if (Tk_IsMapped(dsPtr->tokenwin) && !dsPtr->pkgcmdInProg)
  1078.     {
  1079.       UpdateDDToken((ClientData)dsPtr);
  1080.  
  1081.       if (dsPtr->send)
  1082.         {
  1083.           if (dsPtr->overTargetWin)
  1084.         DragDropSend(dsPtr);
  1085.           else
  1086.         HideDDToken((ClientData)dsPtr);
  1087.         }
  1088.       ddlist->numactive--;  /* one fewer active token window */
  1089.     }
  1090.     }
  1091.  
  1092.   /*
  1093.    *  HANDLE:  blt_drag&drop errors ?<proc>?
  1094.    */
  1095.   else if ((c == 'e') && strncmp(argv[1], "errors", length) == 0)
  1096.     {
  1097.       if (argc == 3)
  1098.     {
  1099.       if (ddlist->errorProc) {
  1100.         free(ddlist->errorProc);
  1101.       }
  1102.       ddlist->errorProc = strdup(argv[2]);
  1103.     }
  1104.       else if (argc != 2)
  1105.     {
  1106.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  1107.                argv[0], " errors ?proc?\"",
  1108.                (char*)NULL);
  1109.       return TCL_ERROR;
  1110.     }
  1111.       Tcl_SetResult(interp, ddlist->errorProc, TCL_VOLATILE);
  1112.       return TCL_OK;
  1113.     }
  1114.  
  1115.   /*
  1116.    *  HANDLE:  blt_drag&drop active
  1117.    */
  1118.   else if ((c == 'a') && strncmp(argv[1], "active", length) == 0)
  1119.     {
  1120.       if (argc != 2)
  1121.     {
  1122.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  1123.                argv[0], " active\"",
  1124.                (char*)NULL);
  1125.       return TCL_ERROR;
  1126.     }
  1127.       Tcl_SetResult(interp, (ddlist->numactive > 0) ? "1" : "0", TCL_STATIC);
  1128.       return TCL_OK;
  1129.     }
  1130.  
  1131.   /*
  1132.    *  HANDLE:  blt_drag&drop location ?<x> <y>?
  1133.    */
  1134.   else if ((c == 'l') && strncmp(argv[1], "location", length) == 0)
  1135.     {
  1136.       if (argc == 2)
  1137.     {
  1138.       sprintf(buffer, "%d %d", ddlist->locx, ddlist->locy);
  1139.       Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1140.       return TCL_OK;
  1141.     }
  1142.       else if ((argc == 4) &&
  1143.            (Tcl_GetInt(interp, argv[2], &x) == TCL_OK) &&
  1144.            (Tcl_GetInt(interp, argv[3], &y) == TCL_OK))
  1145.     {
  1146.       ddlist->locx = x;
  1147.       ddlist->locy = y;
  1148.       sprintf(buffer, "%d %d", ddlist->locx, ddlist->locy);
  1149.       Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1150.       return TCL_OK;
  1151.     }
  1152.       else
  1153.     {
  1154.       Tcl_AppendResult(interp, "wrong # args: should be \"",
  1155.                argv[0], " location ?x y?\"",
  1156.                (char*)NULL);
  1157.       return TCL_ERROR;
  1158.     }
  1159.     }
  1160.  
  1161.   /*
  1162.    *  Report improper command arguments
  1163.    */
  1164.   else
  1165.     {
  1166.       Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be source, target, drag, drop, errors, active or location",
  1167.                (char*)NULL);
  1168.       return TCL_ERROR;
  1169.     }
  1170.   return TCL_OK;
  1171. }
  1172.  
  1173. /*
  1174.  * ------------------------------------------------------------------------
  1175.  *  GetSourceInfo()
  1176.  *
  1177.  *  Looks for a DD_Source record in the hash table for drag&drop source
  1178.  *  widgets.  Creates a new record if the widget name is not already
  1179.  *  registered.  Returns a pointer to the desired record.
  1180.  * ------------------------------------------------------------------------
  1181.  */
  1182. static DD_Source*
  1183. GetSourceInfo(ddlist,pathname,newEntry)
  1184.      DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1185.      char* pathname;      /* widget pathname for desired record */
  1186.      int* newEntry;       /* returns non-zero => new record created */
  1187. {
  1188.   DD_Source *dsPtr;
  1189.   Tcl_HashEntry *ddEntry;
  1190.  
  1191.   ddEntry = Tcl_CreateHashEntry(&ddlist->srcList, pathname, newEntry);
  1192.   if (*newEntry)
  1193.     {
  1194.       /*
  1195.        *  Initialize a data structure for the widget...
  1196.        */
  1197.       dsPtr = (DD_Source*)malloc(sizeof(DD_Source));
  1198.       dsPtr->ddlist = ddlist;
  1199.       dsPtr->display = NULL;
  1200.       dsPtr->tkwin = NULL;
  1201.       dsPtr->ddAtom = None;
  1202.       dsPtr->button = 0;
  1203.  
  1204.       dsPtr->tokenwin = NULL;
  1205.       dsPtr->tokenAnchor = TK_ANCHOR_CENTER;
  1206.       dsPtr->tokenCursor = None;
  1207.       dsPtr->tokenOutline = NULL;
  1208.       dsPtr->tokenBorder = NULL;
  1209.       dsPtr->tokenBorderWidth = 0;
  1210.       dsPtr->rejectFg = NULL;
  1211.       dsPtr->rejectBg = NULL;
  1212.       dsPtr->rejectSt = None;
  1213.       dsPtr->rejectFgGC = None;
  1214.       dsPtr->rejectBgGC = None;
  1215.  
  1216.       dsPtr->pkgcmdInProg = 0;
  1217.       dsPtr->pkgcmd = NULL;
  1218.       dsPtr->pkgcmdResult = NULL;
  1219.       dsPtr->sitecmd = NULL;
  1220.  
  1221.       dsPtr->allwins = NULL;
  1222.       dsPtr->selfTarget = 0;
  1223.       dsPtr->overTargetWin = 0;
  1224.       dsPtr->tokenx = dsPtr->tokeny = 0;
  1225.       dsPtr->hidetoken = NULL;
  1226.       dsPtr->normalCursor = None;
  1227.  
  1228.       dsPtr->send = NULL;
  1229.       dsPtr->handlers = NULL;
  1230.  
  1231.       Tcl_SetHashValue(ddEntry, (ClientData)dsPtr);
  1232.     }
  1233.   return (DD_Source*)Tcl_GetHashValue(ddEntry);
  1234. }
  1235.  
  1236. /*
  1237.  * ------------------------------------------------------------------------
  1238.  *  DestroySourceInfo()
  1239.  *
  1240.  *  Looks for a DD_Source record in the hash table for drag&drop source
  1241.  *  widgets.  Destroys the record if found.
  1242.  * ------------------------------------------------------------------------
  1243.  */
  1244. static void
  1245. DestroySourceInfo(ddlist,pathname)
  1246.      DD_RegList* ddlist;     /* drag&drop records for all registered widgets */
  1247.      char* pathname;         /* widget pathname for desired record */
  1248. {
  1249.   DD_Source *dsPtr;
  1250.   DD_SourceHndl *dsHndl, *next;
  1251.   Tcl_HashEntry *ddEntry;
  1252.  
  1253.   ddEntry = Tcl_FindHashEntry(&ddlist->srcList, pathname);
  1254.   if (ddEntry == NULL) {
  1255.     return;
  1256.   }
  1257.   dsPtr = (DD_Source*)Tcl_GetHashValue(ddEntry);
  1258.   if (dsPtr)
  1259.     {
  1260.       Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1261.       if (dsPtr->hidetoken)           
  1262.     Tk_DeleteTimerHandler(dsPtr->hidetoken);
  1263.  
  1264.       Tk_FreeOptions(SourceConfigSpecs, (char *)dsPtr, dsPtr->display, 0);
  1265.  
  1266.       if (dsPtr->rejectFgGC != None) 
  1267.     Tk_FreeGC(dsPtr->display, dsPtr->rejectFgGC);
  1268.       if (dsPtr->rejectBgGC != None) 
  1269.     Tk_FreeGC(dsPtr->display, dsPtr->rejectBgGC);
  1270.       if (dsPtr->pkgcmdResult)       
  1271.     free(dsPtr->pkgcmdResult);
  1272.  
  1273.       if (dsPtr->allwins)               
  1274.     WinRepRelease(dsPtr->allwins, ddlist);
  1275.  
  1276.       if (dsPtr->normalCursor != None)
  1277.     Tk_FreeCursor(dsPtr->display, dsPtr->normalCursor);
  1278.  
  1279.       dsHndl = dsPtr->handlers;
  1280.       while (dsHndl)
  1281.     {
  1282.       next = dsHndl->next;
  1283. #ifdef STk_CODE
  1284.       DestroySourceHandler(pathname, dsHndl);
  1285. #else
  1286.       DestroySourceHandler(dsHndl);
  1287. #endif
  1288.       dsHndl = next;
  1289.     }
  1290.       free((char*)dsPtr);
  1291.     }
  1292.   Tcl_DeleteHashEntry(ddEntry);
  1293. }
  1294.  
  1295. /*
  1296.  * ------------------------------------------------------------------------
  1297.  *  ConfigSource()
  1298.  *
  1299.  *  Called to process an (argc,argv) list to configure (or reconfigure)
  1300.  *  a drag&drop source widget.
  1301.  * ------------------------------------------------------------------------
  1302.  */
  1303. static int
  1304. ConfigSource(interp, dsPtr, argc, argv, flags)
  1305.      Tcl_Interp *interp;        /* current interpreter */
  1306.      register DD_Source *dsPtr; /* drag&drop source widget record */
  1307.      int argc;                  /* number of arguments */
  1308.      char **argv;               /* argument strings */
  1309.      int flags;                 /* flags controlling interpretation */
  1310. {
  1311.   unsigned long gcMask;
  1312.   XGCValues gcValues;
  1313.   GC newGC;
  1314.  
  1315.   /*
  1316.    *  Handle the bulk of the options...
  1317.    */
  1318.   if (Tk_ConfigureWidget(interp, dsPtr->tkwin, SourceConfigSpecs,
  1319.              argc, argv, (char*)dsPtr, flags) != TCL_OK)
  1320.     return TCL_ERROR;
  1321.  
  1322.   /*
  1323.    *  Check the button binding for valid range (0 or 1-5)
  1324.    */
  1325.   if (dsPtr->button < 0 || dsPtr->button > 5)
  1326.     {
  1327.       Tcl_SetResult(interp,
  1328.             "invalid button binding: should be 1-5 or 0 for no bindings",
  1329.             TCL_STATIC);
  1330.       return TCL_ERROR;
  1331.     }
  1332.   /*
  1333.    *  Set up the rejection foreground GC for the token window...
  1334.    */
  1335.   gcValues.foreground = dsPtr->rejectFg->pixel;
  1336.   gcValues.subwindow_mode = IncludeInferiors;
  1337.   gcValues.graphics_exposures = False;
  1338.   gcMask = GCForeground|GCSubwindowMode|GCGraphicsExposures;
  1339.  
  1340.   if (dsPtr->rejectSt != None)
  1341.     {
  1342.       gcValues.stipple = dsPtr->rejectSt;
  1343.       gcValues.fill_style = FillStippled;
  1344.       gcMask |= GCForeground|GCStipple|GCFillStyle;
  1345.     }
  1346.   newGC = Tk_GetGC(dsPtr->tkwin, gcMask, &gcValues);
  1347.  
  1348.   if (dsPtr->rejectFgGC != None)
  1349.     Tk_FreeGC(dsPtr->display, dsPtr->rejectFgGC);
  1350.   dsPtr->rejectFgGC = newGC;
  1351.  
  1352.   /*
  1353.    *  Set up the rejection background GC for the token window...
  1354.    */
  1355.   gcValues.foreground = dsPtr->rejectBg->pixel;
  1356.   gcValues.subwindow_mode = IncludeInferiors;
  1357.   gcValues.graphics_exposures = False;
  1358.   gcMask = GCForeground|GCSubwindowMode|GCGraphicsExposures;
  1359.  
  1360.   newGC = Tk_GetGC(dsPtr->tkwin, gcMask, &gcValues);
  1361.  
  1362.   if (dsPtr->rejectBgGC != None)
  1363.     Tk_FreeGC(dsPtr->display, dsPtr->rejectBgGC);
  1364.   dsPtr->rejectBgGC = newGC;
  1365.  
  1366.   /*
  1367.    *  Reset the border width in case it has changed...
  1368.    */
  1369.   if (dsPtr->tokenwin)
  1370.     Tk_SetInternalBorder(dsPtr->tokenwin, 2*dsPtr->tokenBorderWidth);
  1371.  
  1372.   return TCL_OK;
  1373. }
  1374.  
  1375. /*
  1376.  * ------------------------------------------------------------------------
  1377.  *  FindSourceHandler()
  1378.  *
  1379.  *  Looks for the requested data type of the list of handlers for the
  1380.  *  given drag&drop source.
  1381.  * ------------------------------------------------------------------------
  1382.  */
  1383. static char*
  1384. FindSourceHandler(dsPtr, dtname)
  1385.      register DD_Source *dsPtr; /* drag&drop source widget record */
  1386.      char *dtname;              /* name of requested data type */
  1387. {
  1388.   DD_SourceHndl *dsHndl;
  1389.  
  1390.   for (dsHndl=dsPtr->handlers; dsHndl; dsHndl=dsHndl->next)
  1391.     if (strcmp(dsHndl->dataType,dtname) == 0)
  1392.       return dsHndl->cmd;
  1393.  
  1394.   return NULL;
  1395. }
  1396.  
  1397. /*
  1398.  * ------------------------------------------------------------------------
  1399.  *  PutSourceHandler()
  1400.  *
  1401.  *  Looks for the requested data type of the list of handlers for the
  1402.  *  given drag&drop source.  If found, then its associated commands are
  1403.  *  changed to the given commands.  If not found, then a new handler
  1404.  *  is created.
  1405.  * ------------------------------------------------------------------------
  1406.  */
  1407. static void
  1408. PutSourceHandler(dsPtr, dtname, cmd)
  1409.      register DD_Source *dsPtr; /* drag&drop target widget record */
  1410.      char *dtname;              /* name of data type */
  1411.      char *cmd;                 /* command used to send data */
  1412. {
  1413.   DD_SourceHndl *tail = NULL;
  1414.   DD_SourceHndl *dsHndl;
  1415.  
  1416.   for (dsHndl=dsPtr->handlers; dsHndl; tail=dsHndl,dsHndl=dsHndl->next)
  1417.     if (strcmp(dsHndl->dataType,dtname) == 0)
  1418.       {
  1419.     if (*cmd == '\0')
  1420.       {
  1421.         if (tail)
  1422.           tail->next = dsHndl->next;
  1423.         else
  1424.           dsPtr->handlers = dsHndl->next;
  1425.  
  1426. #ifdef STk_CODE
  1427.         DestroySourceHandler(Tk_PathName(dsPtr->tkwin), dsHndl);
  1428. #else
  1429.         DestroySourceHandler(dsHndl);
  1430. #endif
  1431.         return;
  1432.       }
  1433.     else
  1434.       {
  1435.         if (dsHndl->cmd != NULL) {
  1436.           free(dsHndl->cmd);
  1437.         }
  1438.         dsHndl->cmd = strdup(cmd);
  1439. #ifdef STk_CODE
  1440.         goto Register_callback;
  1441. #else
  1442.         return;
  1443. #endif
  1444.       }
  1445.       }
  1446.  
  1447.   if (tail)
  1448.     tail->next = CreateSourceHandler(dtname,cmd);
  1449.   else
  1450.     dsPtr->handlers = CreateSourceHandler(dtname,cmd);
  1451.  
  1452. #ifdef STk_CODE
  1453. Register_callback:
  1454.   /* Add this handler to the callback table */
  1455.   {
  1456.     SCM p;
  1457.     
  1458.     if (!STk_valid_callback(cmd, &p)) {
  1459.       /* Not as usual. Should be reworked */
  1460.       fprintf(stderr, "*** WARNING: bad closure specification \"%s\"\n", cmd);
  1461.       return;
  1462.     }
  1463.     if (p != NULL)
  1464.       /* add this closure to the callback table */
  1465.       STk_add_callback(Tk_PathName(dsPtr->tkwin), "source", dtname, p);
  1466.   }
  1467. #endif
  1468. }
  1469.  
  1470. /*
  1471.  * ------------------------------------------------------------------------
  1472.  *  CreateSourceHandler()
  1473.  *
  1474.  *  Creates a new source handler record and returns a pointer to it.
  1475.  * ------------------------------------------------------------------------
  1476.  */
  1477. static DD_SourceHndl*
  1478. CreateSourceHandler(dtname, cmd)
  1479.      char *dtname;              /* name of data type */
  1480.      char *cmd;                 /* command used to send data */
  1481. {
  1482.   DD_SourceHndl *retn;
  1483.   retn = (DD_SourceHndl*)malloc(sizeof(DD_SourceHndl));
  1484.  
  1485.   retn->dataType = strdup(dtname);
  1486.   retn->cmd = strdup(cmd);
  1487.   retn->next = NULL;
  1488.   return retn;
  1489. }
  1490.  
  1491. /*
  1492.  * ------------------------------------------------------------------------
  1493.  *  DestroySourceHandler()
  1494.  *
  1495.  *  Destroys a source handler record.
  1496.  * ------------------------------------------------------------------------
  1497.  */
  1498. static void
  1499. #ifdef STk_CODE
  1500. DestroySourceHandler(path, dsHndl)
  1501.      char *path;
  1502.      DD_SourceHndl *dsHndl;
  1503. #else
  1504. DestroySourceHandler(dsHndl)
  1505.      DD_SourceHndl *dsHndl;
  1506. #endif
  1507. {
  1508. #ifdef STk_CODE
  1509.   /* We don't delete really the delete the old callback entry because STk 
  1510.    * don't provide a way to do so (it only provide a way to delete
  1511.    * *all* the entries associated to path. Instead, we set our new
  1512.    * callback to #f, to allow GC'ing old callback
  1513.    */
  1514.   extern SCM STk_ntruth; 
  1515.  
  1516.   STk_add_callback(path, "source", dsHndl->dataType, STk_ntruth);
  1517. #endif
  1518.   if (dsHndl->dataType != NULL) {
  1519.     free(dsHndl->dataType);
  1520.   }
  1521.   if (dsHndl->cmd != NULL) {
  1522.     free(dsHndl->cmd);
  1523.   }
  1524.   free((char*)dsHndl);
  1525. }
  1526.  
  1527. /*
  1528.  * ------------------------------------------------------------------------
  1529.  *  UnregSource()
  1530.  *
  1531.  *  Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
  1532.  *  on a registered drag&drop source widget.
  1533.  * ------------------------------------------------------------------------
  1534.  */
  1535. static void
  1536. UnregSource(cdata, eventPtr)
  1537.      ClientData cdata;   /* drag&drop registration list */
  1538.      XEvent *eventPtr;   /* event description */
  1539. {
  1540.   DD_RegEntry *ddentry = (DD_RegEntry*)cdata;
  1541.   DD_RegList *ddlist = ddentry->ddlist;
  1542.   char *ddname = Tk_PathName(ddentry->tkwin);
  1543.  
  1544.   if (eventPtr->type == DestroyNotify)
  1545.     {
  1546.       DestroySourceInfo(ddlist,ddname);
  1547.       free((char*)ddentry);
  1548.     }
  1549. }
  1550.  
  1551. /*
  1552.  * ------------------------------------------------------------------------
  1553.  *  GetTargetInfo()
  1554.  *
  1555.  *  Looks for a DD_Target record in the hash table for drag&drop target
  1556.  *  widgets.  Creates a new record if the widget name is not already
  1557.  *  registered.  Returns a pointer to the desired record.
  1558.  * ------------------------------------------------------------------------
  1559.  */
  1560. static DD_Target*
  1561. GetTargetInfo(ddlist,pathname,newEntry)
  1562.      DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1563.      char* pathname;      /* widget pathname for desired record */
  1564.      int* newEntry;       /* returns non-zero => new record created */
  1565. {
  1566.   DD_Target *dtPtr;
  1567.   Tcl_HashEntry *ddEntry;
  1568.  
  1569.   ddEntry = Tcl_CreateHashEntry(&ddlist->trgList, pathname, newEntry);
  1570.   if (*newEntry)
  1571.     {
  1572.       /*
  1573.        *  Initialize a data structure for the widget...
  1574.        */
  1575.       dtPtr = (DD_Target*)malloc(sizeof(DD_Target));
  1576.       dtPtr->ddlist = ddlist;
  1577.       dtPtr->display = NULL;
  1578.       dtPtr->tkwin = NULL;
  1579.       dtPtr->handlers = NULL;
  1580.  
  1581.       Tcl_SetHashValue(ddEntry, (ClientData)dtPtr);
  1582.     }
  1583.   return (DD_Target*)Tcl_GetHashValue(ddEntry);
  1584. }
  1585.  
  1586. /*
  1587.  * ------------------------------------------------------------------------
  1588.  *  DestroyTargetInfo()
  1589.  *
  1590.  *  Looks for a DD_Target record in the hash table for drag&drop target
  1591.  *  widgets.  Destroys the record if found.
  1592.  * ------------------------------------------------------------------------
  1593.  */
  1594. static void
  1595. DestroyTargetInfo(ddlist,pathname)
  1596.      DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1597.      char* pathname;      /* widget pathname for desired record */
  1598. {
  1599.   DD_Target *dtPtr;
  1600.   DD_TargetHndl *dtHndl, *next;
  1601.   Tcl_HashEntry *ddEntry;
  1602.  
  1603.   ddEntry = Tcl_FindHashEntry(&ddlist->trgList, pathname);
  1604.   dtPtr = (ddEntry) ? (DD_Target*)Tcl_GetHashValue(ddEntry) : NULL;
  1605.  
  1606.   if (dtPtr)
  1607.     {
  1608.       dtHndl = dtPtr->handlers;
  1609.       while (dtHndl)
  1610.     {
  1611.       next = dtHndl->next;
  1612. #ifdef STk_CODE
  1613.       DestroyTargetHandler(pathname, dtHndl);
  1614. #else
  1615.       DestroyTargetHandler(dtHndl);
  1616. #endif
  1617.       dtHndl = next;
  1618.     }
  1619.       free((char*)dtPtr);
  1620.     }
  1621.   if (ddEntry) Tcl_DeleteHashEntry(ddEntry);
  1622. }
  1623.  
  1624. /*
  1625.  * ------------------------------------------------------------------------
  1626.  *  FindTargetHandler()
  1627.  *
  1628.  *  Looks for the requested data type of the list of handlers for the
  1629.  *  given drag&drop target.
  1630.  * ------------------------------------------------------------------------
  1631.  */
  1632. static char*
  1633. FindTargetHandler(dtPtr, dtname)
  1634.      register DD_Target *dtPtr; /* drag&drop target widget record */
  1635.      char *dtname;              /* name of requested data type */
  1636. {
  1637.   DD_TargetHndl *dtHndl;
  1638.  
  1639.   for (dtHndl=dtPtr->handlers; dtHndl; dtHndl=dtHndl->next)
  1640.     if (strcmp(dtHndl->dataType,dtname) == 0)
  1641.       return dtHndl->command;
  1642.  
  1643.   return NULL;
  1644. }
  1645.  
  1646. /*
  1647.  * ------------------------------------------------------------------------
  1648.  *  PutTargetHandler()
  1649.  *
  1650.  *  Looks for the requested data type of the list of handlers for the
  1651.  *  given drag&drop target.  If found, then its associated command is
  1652.  *  changed to the given command.  If not found, then a new handler
  1653.  *  is created.
  1654.  * ------------------------------------------------------------------------
  1655.  */
  1656. static void
  1657. PutTargetHandler(dtPtr, dtname, cmd)
  1658.      register DD_Target *dtPtr; /* drag&drop target widget record */
  1659.      char *dtname;              /* name of data type */
  1660.      char *cmd;                 /* command string for data type */
  1661. {
  1662.   DD_TargetHndl *tail = NULL;
  1663.   DD_TargetHndl *dtHndl;
  1664.  
  1665.   for (dtHndl=dtPtr->handlers; dtHndl; tail=dtHndl,dtHndl=dtHndl->next)
  1666.     if (strcmp(dtHndl->dataType,dtname) == 0)
  1667.       {
  1668.     if (*cmd == '\0')
  1669.       {
  1670.         if (tail)
  1671.           tail->next = dtHndl->next;
  1672.         else
  1673.           dtPtr->handlers = dtHndl->next;
  1674.  
  1675. #ifdef STk_CODE
  1676.         DestroyTargetHandler(Tk_PathName(dtPtr->tkwin), dtHndl);
  1677. #else
  1678.         DestroyTargetHandler(dtHndl);
  1679. #endif
  1680.         return;
  1681.       }
  1682.     else
  1683.       {
  1684.         if (dtHndl->command != NULL) {
  1685.           free(dtHndl->command);
  1686.         }
  1687.         dtHndl->command = strdup(cmd);
  1688. #ifdef STk_CODE
  1689.         goto Register_callback;
  1690. #else
  1691.         return;
  1692. #endif
  1693.       }
  1694.       }
  1695.  
  1696.   if (tail)
  1697.     tail->next = CreateTargetHandler(dtname,cmd);
  1698.   else
  1699.     dtPtr->handlers = CreateTargetHandler(dtname,cmd);
  1700.  
  1701.   /*
  1702.    *  Update handler list stored in target window property.
  1703.    */
  1704.   AddDDProp(dtPtr);
  1705.  
  1706. #ifdef STk_CODE
  1707. Register_callback:
  1708.   /* Add this handler to the callback table */
  1709.   {
  1710.     SCM p;
  1711.     
  1712.     if (!STk_valid_callback(cmd, &p)) {
  1713.       /* Not as usual. Should be reworked */
  1714.       fprintf(stderr, "*** WARNING: bad closure specification \"%s\"\n", cmd);
  1715.       return;
  1716.     }
  1717.     if (p != NULL)
  1718.       /* add this closure to the callback table */
  1719.       STk_add_callback(Tk_PathName(dtPtr->tkwin), "target", dtname, p);
  1720.   }
  1721. #endif
  1722. }
  1723.  
  1724. /*
  1725.  * ------------------------------------------------------------------------
  1726.  *  CreateTargetHandler()
  1727.  *
  1728.  *  Creates a new target handler record and returns a pointer to it.
  1729.  * ------------------------------------------------------------------------
  1730.  */
  1731. static DD_TargetHndl*
  1732. CreateTargetHandler(dtname, cmd)
  1733.      char *dtname;              /* name of data type */
  1734.      char *cmd;                 /* command string for data type */
  1735. {
  1736.   DD_TargetHndl *retn;
  1737.   retn = (DD_TargetHndl*)malloc(sizeof(DD_TargetHndl));
  1738.  
  1739.   retn->dataType = strdup(dtname);
  1740.   retn->command = strdup(cmd);
  1741.  
  1742.   retn->next = NULL;
  1743.   return retn;
  1744. }
  1745.  
  1746. /*
  1747.  * ------------------------------------------------------------------------
  1748.  *  DestroyTargetHandler()
  1749.  *
  1750.  *  Destroys a target handler record.
  1751.  * ------------------------------------------------------------------------
  1752.  */
  1753. static void
  1754. #ifdef STk_CODE
  1755. DestroyTargetHandler(path, dtHndl)
  1756.      char *path;
  1757.      DD_TargetHndl *dtHndl;
  1758. #else
  1759. DestroyTargetHandler(dtHndl)
  1760.      DD_TargetHndl *dtHndl;
  1761. #endif
  1762. {
  1763. #ifdef STk_CODE
  1764.   /* We don't delete really delete the old callback entry because STk 
  1765.    * don't provide a way to do so (it only provide a way to delete
  1766.    * *all* the entries associated to path. Instead, we set our new
  1767.    * callback to #f, to allow GC'ing old callback
  1768.    */
  1769.   extern SCM STk_ntruth; 
  1770.  
  1771.   STk_add_callback(path, "target", dtHndl->dataType, STk_ntruth);
  1772. #endif
  1773.   if (dtHndl->dataType != NULL) {
  1774.     free(dtHndl->dataType);
  1775.   }
  1776.   if (dtHndl->command != NULL) {
  1777.     free(dtHndl->command);
  1778.   }
  1779.   free((char*)dtHndl);
  1780. }
  1781.  
  1782. /*
  1783.  * ------------------------------------------------------------------------
  1784.  *  UnregTarget()
  1785.  *
  1786.  *  Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
  1787.  *  on a registered drag&drop target widget.
  1788.  * ------------------------------------------------------------------------ */
  1789. static void
  1790. UnregTarget(cdata, eventPtr)
  1791.      ClientData cdata;   /* drag&drop registration list */
  1792.      XEvent *eventPtr;   /* event description */
  1793. {
  1794.   DD_RegEntry *ddentry = (DD_RegEntry*)cdata;
  1795.   DD_RegList *ddlist = ddentry->ddlist;
  1796.   char *ddname = Tk_PathName(ddentry->tkwin);
  1797.  
  1798.   if (eventPtr->type == DestroyNotify)
  1799.     {
  1800.       DestroyTargetInfo(ddlist,ddname);
  1801.       free((char*)ddentry);
  1802.     }
  1803. }
  1804.  
  1805. /*
  1806.  * ------------------------------------------------------------------------
  1807.  *  DragDropSend()
  1808.  *
  1809.  *  Invoked after a drop operation to send data to the drop application.
  1810.  * ------------------------------------------------------------------------
  1811.  */
  1812. static void
  1813. DragDropSend(dsPtr)
  1814.      register DD_Source *dsPtr;  /* drag&drop source record */
  1815. {
  1816.   DD_RegList *ddlist = dsPtr->ddlist;
  1817.  
  1818.   int status;
  1819.   char *sendcmd;
  1820.   DD_WinRep *target;
  1821.  
  1822.   /*
  1823.    *  See if current position is over drop point...
  1824.    */
  1825.   target = FindTargetWin(dsPtr, dsPtr->tokenx,dsPtr->tokeny);
  1826.  
  1827.   if (target)
  1828.     {
  1829.       char buffer[256];
  1830.  
  1831.       sprintf(buffer, "%d %d", dsPtr->tokenx, dsPtr->tokeny);
  1832.       status = Tcl_VarEval(ddlist->interp,
  1833. #ifdef STk_CODE
  1834.                "(send ",target->ddinterp," '(", DRAGDROP_COMMAND," 'location ",buffer,"))", 
  1835. #else
  1836.                "send {",target->ddinterp,"} ", DRAGDROP_COMMAND," location ",buffer,
  1837. #endif
  1838.                (char*)NULL);
  1839.  
  1840.       if (status == TCL_OK)
  1841.     {
  1842.       sendcmd = DragDropSendHndlr(dsPtr, target->ddinterp, target->ddwin);
  1843.       if (sendcmd)
  1844.         {
  1845.           status = Tcl_VarEval(ddlist->interp,
  1846. #ifdef STk_CODE
  1847.                    "(", sendcmd, " ",target->ddinterp," '",target->ddwin,
  1848.                    " '",dsPtr->pkgcmdResult,")",
  1849. #else
  1850.                    sendcmd, " {",target->ddinterp,"} {",target->ddwin,
  1851.                    "} {",dsPtr->pkgcmdResult,"}",
  1852. #endif                             
  1853.                    (char*)NULL);
  1854.         }
  1855.       else
  1856.         {
  1857.           Tcl_AppendResult(ddlist->interp, "target \"",
  1858.                    target->ddwin,
  1859.                    "\" does not recognize handlers for source \"",
  1860.                    Tk_PathName(dsPtr->tkwin), "\"",
  1861.                    (char*)NULL);
  1862.           status = TCL_ERROR;
  1863.         }
  1864.     }
  1865.  
  1866.       /*
  1867.        *  Give success/failure feedback to user.
  1868.        *  If an error occurred and an error proc is defined,
  1869.        *  then use it to handle the error.
  1870.        */
  1871.       if (status == TCL_OK)
  1872.     HideDDToken((ClientData)dsPtr);
  1873.       else
  1874.     {
  1875.       RejectDDToken(dsPtr);
  1876.  
  1877.       if (ddlist->errorProc && *ddlist->errorProc)
  1878.         (void) Tcl_VarEval(ddlist->interp,
  1879. #ifdef STk_CODE
  1880.                    "(", ddlist->errorProc, " ", 
  1881.                    (char *) STk_stringify(ddlist->interp->result, 0), ")",
  1882. #else
  1883.                    ddlist->errorProc, " {", ddlist->interp->result, "}",
  1884. #endif
  1885.                    (char*)NULL);
  1886.     }
  1887.     }
  1888. }
  1889.  
  1890. /*
  1891.  * ------------------------------------------------------------------------
  1892.  *  DragDropSendHndlr()
  1893.  *
  1894.  *  Queries the drag&drop target under the specified interpreter for a
  1895.  *  handler that is compatible with one of the handlers defined for the
  1896.  *  source.  Returns a pointer to the appropriate send command, or
  1897.  *  NULL if none is found.
  1898.  * ------------------------------------------------------------------------
  1899.  */
  1900. static char*
  1901. DragDropSendHndlr(dsPtr,interpName,ddName)
  1902.      register DD_Source *dsPtr;  /* drag&drop source record */
  1903.      char *interpName;           /* interpreter containing drag&drop target */
  1904.      char *ddName;               /* drag&drop target pathname */
  1905. {
  1906.   char *retn = NULL;        /* no handler found yet */
  1907.   Tcl_Interp *interp = dsPtr->ddlist->interp;
  1908.  
  1909.   int hndlc, hi, ei;
  1910.   char **hndlv, *hlist;
  1911.   DD_SourceHndl *dsHndl;
  1912.   char buffer[1024];
  1913.  
  1914.   /*
  1915.    *  Query the drag&drop target for its list of known handlers.
  1916.    */
  1917.   Tcl_ResetResult(interp);    /* for Tcl_AppendResult() below */
  1918.   if (Tcl_VarEval(interp,
  1919. #ifdef STk_CODE
  1920.           "(send ",interpName," '(", DRAGDROP_COMMAND," 'target '",ddName," 'handler))",
  1921. #else
  1922.           "send {",interpName,"} ", DRAGDROP_COMMAND," target {",ddName,"} handler",
  1923. #endif
  1924.           (char*)NULL) != TCL_OK)
  1925.     return NULL;
  1926. #ifdef STk_CODE
  1927.     /* DIRTY HACKS..... But I don't know how to make it work better */
  1928.     /* Result of send is always a string in STk. So there are '"' to delete */
  1929.     /* Furthermore, The STk Tcl_SplitList doesn't handle very well lists */
  1930.     if (*(interp->result)) {
  1931.       int x = ((interp->result)[1]) == '('? 2: 1;
  1932.  
  1933.       hlist = strdup(interp->result + x);
  1934.       hlist[strlen(hlist)-x] = '\0';
  1935.     }
  1936.     else 
  1937.       hlist = strdup(interp->result);
  1938. #else
  1939.   hlist = strdup(interp->result);
  1940. #endif
  1941.   if (Tcl_SplitList(interp, hlist, &hndlc, &hndlv) == TCL_OK)
  1942.     {
  1943.       /*
  1944.        *  If the list of send handlers is specified as "all", then
  1945.        *  search through the handlers in order.
  1946.        */
  1947.       if (strcmp(dsPtr->send,"all") == 0)
  1948.     for (dsHndl=dsPtr->handlers; dsHndl && !retn; dsHndl=dsHndl->next)
  1949.       {
  1950.         for (hi=0; (hi < hndlc) && !retn; hi++)
  1951.           if (strcmp(dsHndl->dataType, hndlv[hi]) == 0)
  1952.         retn = dsHndl->cmd;
  1953.       }
  1954.  
  1955.       /*
  1956.        *  Otherwise, search through the specified send handlers.
  1957.        */
  1958.       else
  1959.     {
  1960.       int elemc;
  1961.       char **elemv;
  1962.       if (Tcl_SplitList(interp,dsPtr->send,&elemc,&elemv)==TCL_OK)
  1963.         {
  1964.           for (ei=0; (ei < elemc) && !retn; ei++)
  1965.         for (hi=0; (hi < hndlc) && !retn; hi++)
  1966.           if (strcmp(elemv[ei], hndlv[hi]) == 0)
  1967.             {
  1968.               retn = FindSourceHandler(dsPtr, elemv[ei]);
  1969.               if (!retn)
  1970.             {
  1971.               sprintf(buffer, "unknown handler \"%.50s\" requested for drag&drop source \"%.200s\"", elemv[ei], Tk_PathName(dsPtr->tkwin));
  1972.               Tcl_ResetResult(interp);
  1973.               Tcl_AddErrorInfo(interp, buffer);
  1974.               Tk_BackgroundError(interp);
  1975.             }
  1976.             }
  1977.  
  1978.           free((char*)elemv);
  1979.         }
  1980.       else
  1981.         {
  1982.           sprintf(buffer, "drag&drop source has invalid -send: %.200s",
  1983.               dsPtr->send);
  1984.           Tcl_ResetResult(interp);
  1985.           Tcl_AddErrorInfo(interp, buffer);
  1986.           Tk_BackgroundError(interp);
  1987.         }
  1988.     }
  1989.       free((char*)hndlv);
  1990.     }
  1991.   free(hlist);
  1992.  
  1993.   return retn;
  1994. }
  1995.  
  1996.  
  1997. /*
  1998.  * ------------------------------------------------------------------------
  1999.  *  GetWinRepInfo()
  2000.  *
  2001.  *  Invoked at the start of a "drag" operation to capture the positions
  2002.  *  of all windows on the current root.  Queries the entire window
  2003.  *  hierarchy and determines the placement of each window.  Queries
  2004.  *  "DragDropInfo" property info where appropriate.  This information
  2005.  *  is used during the drag operation to determine when the drag&drop
  2006.  *  token is over a valid drag&drop target.
  2007.  *
  2008.  *  Returns the record for the root window, which contains records for
  2009.  *  all other windows as children.
  2010.  * ------------------------------------------------------------------------
  2011.  */
  2012. static DD_WinRep*
  2013. GetWinRepInfo(dsPtr,ddlist)
  2014.      DD_Source *dsPtr;     /* drag&drop source window */
  2015.      DD_RegList *ddlist;   /* drag&drop registration info */
  2016. {
  2017.   DD_WinRep *wr;
  2018.  
  2019.   wr = WinRepAlloc(ddlist);
  2020.   wr->win = DefaultRootWindow(dsPtr->display);
  2021.   WinRepInit(wr, dsPtr);
  2022.  
  2023.   return wr;
  2024. }
  2025.  
  2026. /*
  2027.  * ------------------------------------------------------------------------
  2028.  *  FindTargetWin()
  2029.  *
  2030.  *  Checks to see if a compatible drag&drop target exists at the given
  2031.  *  position.  A target is "compatible" if it is a drag&drop window,
  2032.  *  and if it has a handler that is compatible with the current source
  2033.  *  window.
  2034.  *
  2035.  *  Returns a pointer to a structure describing the target, or NULL
  2036.  *  if no compatible target is found.
  2037.  * ------------------------------------------------------------------------
  2038.  */
  2039. static DD_WinRep*
  2040. FindTargetWin(dsPtr,x,y)
  2041.      DD_Source *dsPtr;     /* drag&drop source window */
  2042.      int x,y;              /* current drag&drop location (virtual coords) */
  2043. {
  2044.   int vx, vy, vw, vh;
  2045.   register char *type;
  2046.  
  2047.   DD_WinRep *wr, *wrkid;
  2048.   DD_Stack stack;
  2049.   DD_SourceHndl *shandl;
  2050.  
  2051.   /*
  2052.    *  If window representations have not yet been built, then
  2053.    *  abort this call.  This probably means that the token is being
  2054.    *  moved before it has been properly built.
  2055.    */
  2056.   if (!dsPtr->allwins)
  2057.     return NULL;
  2058.  
  2059.   /*
  2060.    *  Adjust current location for virtual root windows.
  2061.    */
  2062.   Tk_GetVRootGeometry(dsPtr->tkwin, &vx, &vy, &vw, &vh);
  2063.   x += vx;
  2064.   y += vy;
  2065.  
  2066.   /*
  2067.    *  Build a stack of all windows containing the given point,
  2068.    *  in order from least to most specific.
  2069.    */
  2070.   StackInit(&stack);
  2071.  
  2072.   wr = dsPtr->allwins;
  2073.   if ((x >= wr->x0) && (x <= wr->x1) &&
  2074.       (y >= wr->y0) && (y <= wr->y1))
  2075.     StackPush((ClientData)wr, &stack);
  2076.  
  2077.   while (wr)
  2078.     {
  2079.       for (wrkid=wr->kids; wrkid; wrkid=wrkid->next)
  2080.     {
  2081.       if (!wrkid->initialized)
  2082.         WinRepInit(wrkid, dsPtr);
  2083.  
  2084.       if ((x >= wrkid->x0) && (x <= wrkid->x1) &&
  2085.           (y >= wrkid->y0) && (y <= wrkid->y1))
  2086.         {
  2087.           StackPush((ClientData)wrkid, &stack);
  2088.           break;
  2089.         }
  2090.     }
  2091.       wr = wrkid;        /* continue search */
  2092.     }
  2093.  
  2094.   /*
  2095.    *  Pop windows from the stack until one containing a
  2096.    *  "DragDropInfo" property is found.  See if the handlers
  2097.    *  listed in this property are compatible with the
  2098.    *  given source.
  2099.    */
  2100.   while ((wr=(DD_WinRep*)StackPop(&stack)) != NULL)
  2101.     if (wr->ddprop)
  2102.       break;
  2103.  
  2104.   if (wr && wr->ddhandlers)
  2105.     {
  2106.       type = wr->ddhandlers;
  2107.       while (*type != '\0')
  2108.     {
  2109.       for (shandl=dsPtr->handlers; shandl; shandl=shandl->next)
  2110.         if (strcmp(shandl->dataType, type) == 0)
  2111.           break;
  2112.  
  2113.       if (shandl)        /* found a match? */
  2114.         break;        /* then stop searching */
  2115.       else            /* otherwise, move to next handler type */
  2116.         {
  2117.           while (*type++ != '\0')
  2118.         ;
  2119.         }
  2120.     }
  2121.       if (*type == '\0')    /* no handler match? */
  2122.     wr = NULL;        /* then return NULL */
  2123.     }
  2124.   StackDelete(&stack);
  2125.  
  2126.   return wr;
  2127. }
  2128.  
  2129.  
  2130. /*
  2131.  * ------------------------------------------------------------------------
  2132.  *  WinRepAlloc()
  2133.  *
  2134.  *  Returns a new structure for representing X window position info.
  2135.  *  Such structures are typically allocated at the start of a drag&drop
  2136.  *  operation to capture the placement of all windows on the root
  2137.  *  window.  The drag&drop registration list keeps a pool of such
  2138.  *  structures so that they can be allocated quickly when needed.
  2139.  *  Returns a pointer to an empty structure.
  2140.  * ------------------------------------------------------------------------
  2141.  */
  2142. static DD_WinRep*
  2143. WinRepAlloc(ddlist)
  2144.      DD_RegList *ddlist;  /* drag&drop registration list */
  2145. {
  2146.   DD_WinRep *wr;
  2147.  
  2148.   /*
  2149.    *  Return the top-most structure in the pool.
  2150.    *  If the pool is empty, add a new structure to it.
  2151.    */
  2152.   if (!ddlist->pool)
  2153.     {
  2154.       wr = (DD_WinRep*)malloc(sizeof(DD_WinRep));
  2155.       wr->next = NULL;
  2156.       ddlist->pool = wr;
  2157.     }
  2158.   wr = ddlist->pool;
  2159.   ddlist->pool = wr->next;
  2160.  
  2161.   wr->initialized = 0;
  2162.   wr->ddprop = NULL;
  2163.   wr->ddinterp = wr->ddwin = wr->ddhandlers = NULL;
  2164.   wr->parent = wr->kids = wr->next = NULL;
  2165.   return wr;
  2166. }
  2167.  
  2168. /*
  2169.  * ------------------------------------------------------------------------
  2170.  *  WinRepRelease()
  2171.  *
  2172.  *  Puts a window representation structure back into the global pool,
  2173.  *  making it available for future calls to WinRepAlloc().  Any
  2174.  *  associated resources (within the structure) are automatically freed.
  2175.  * ------------------------------------------------------------------------
  2176.  */
  2177. static void
  2178. WinRepRelease(wr,ddlist)
  2179.      DD_WinRep *wr;       /* window rep to be freed */
  2180.      DD_RegList *ddlist;  /* drag&drop registration list */
  2181. {
  2182.   DD_WinRep *wrkid, *wrnext;
  2183.  
  2184.   for (wrkid=wr->kids; wrkid; wrkid=wrnext)
  2185.     {
  2186.       wrnext = wrkid->next;
  2187.       WinRepRelease(wrkid,ddlist);
  2188.     }
  2189.  
  2190.   if (wr->ddprop)
  2191.     XFree(wr->ddprop);
  2192.  
  2193.   wr->next = ddlist->pool;    /* put back into pool */
  2194.   ddlist->pool = wr;
  2195. }
  2196.  
  2197.  
  2198. /*
  2199.  * ------------------------------------------------------------------------
  2200.  *  WinRepInit()
  2201.  *
  2202.  *  Invoked during "drag" operations to dig a little deeper into the
  2203.  *  root window hierarchy and cache the resulting information.  If a
  2204.  *  point coordinate lies within an uninitialized DD_WinRep, this
  2205.  *  routine is called to query window coordinates and drag&drop info.
  2206.  *  If this particular window has any children, more uninitialized
  2207.  *  DD_WinRep structures are allocated.  Further queries will cause
  2208.  *  these structures to be initialized in turn.
  2209.  * ------------------------------------------------------------------------
  2210.  */
  2211. static void
  2212. WinRepInit(wr,dsPtr)
  2213.      DD_WinRep *wr;         /* window rep to be initialized */
  2214.      DD_Source *dsPtr;      /* drag&drop source managing win rep */
  2215. {
  2216.   Window ignoreSource = Tk_WindowId(dsPtr->tkwin);
  2217.   Window ignoreToken  = Tk_WindowId(dsPtr->tokenwin);
  2218.  
  2219.   DD_WinRep *wrkid, *wrtail;
  2220.  
  2221.   Window root, parent, *kids;
  2222.   unsigned int nkids;
  2223.   XWindowAttributes winInfo;
  2224.  
  2225.   char *propInfo;
  2226.   int i, result, actualFormat;
  2227.   Atom actualType;
  2228.   unsigned long numItems, bytesAfter;
  2229.  
  2230.   /*
  2231.    *  If the self-target flag is set, allow the source window to
  2232.    *  drop onto itself.  Do not ignore source window during search.
  2233.    */
  2234.   if (dsPtr->selfTarget)
  2235.     ignoreSource = None;
  2236.  
  2237.   if (!wr->initialized)
  2238.     {
  2239.       /*
  2240.        *  Query for the window coordinates.
  2241.        */
  2242.       if (XGetWindowAttributes(dsPtr->display, wr->win, &winInfo) &&
  2243.       (winInfo.map_state == IsViewable) &&
  2244.       (wr->win != ignoreToken) &&
  2245.       (wr->win != ignoreSource))
  2246.     {
  2247.       wr->x0  = winInfo.x;
  2248.       wr->y0  = winInfo.y;
  2249.       wr->x1  = winInfo.x + winInfo.width;
  2250.       wr->y1  = winInfo.y + winInfo.height;
  2251.  
  2252.       if (wr->parent)    /* offset by parent coords */
  2253.         {
  2254.           wr->x0 += wr->parent->x0;
  2255.           wr->y0 += wr->parent->y0;
  2256.           wr->x1 += wr->parent->x0;
  2257.           wr->y1 += wr->parent->y0;
  2258.         }
  2259.     }
  2260.       else
  2261.     {
  2262.       wr->x0 = wr->y0 = -1;
  2263.       wr->x1 = wr->y1 = -1;
  2264.     }
  2265.  
  2266.       /*
  2267.        *  See if this window has a "DragDropInfo" property.
  2268.        */
  2269.       result = XGetWindowProperty(dsPtr->display, wr->win,
  2270.                   dsPtr->ddAtom, 0, MAX_PROP_SIZE, False, XA_STRING,
  2271.                   &actualType, &actualFormat,
  2272.                   &numItems, &bytesAfter, (unsigned char**)&propInfo);
  2273.  
  2274.       if ((result != Success) ||
  2275.       (actualFormat != 8) ||
  2276.       (actualType != XA_STRING))
  2277.     {
  2278.       if (propInfo != NULL) {
  2279.         XFree((caddr_t)propInfo);
  2280.       }
  2281.       propInfo = NULL;
  2282.     }
  2283.  
  2284.       wr->ddprop = propInfo;
  2285.       if (wr->ddprop)
  2286.     {
  2287.       char *p = wr->ddprop;
  2288.       wr->ddinterp = wr->ddprop;
  2289.  
  2290. #ifdef STk_CODE
  2291.       while ((*p != '\0') && (*p != '}'))
  2292. #else
  2293.       while ((*p != '\0') && (*p != ']'))
  2294. #endif
  2295.         p++;
  2296.  
  2297.       if (*p != '\0')
  2298.         {
  2299.           *p++ = '\0';    /* terminate interp name */
  2300.           wr->ddwin = p;    /* get start of window name */
  2301.         }
  2302.  
  2303. #ifdef STk_CODE
  2304.       while ((*p != '\0') && (*p != '}'))
  2305. #else
  2306.       while ((*p != '\0') && (*p != ']'))
  2307. #endif
  2308.         p++;
  2309.  
  2310.       if (*p != '\0')
  2311.         {
  2312.           *p++ = '\0';    /* terminate window name */
  2313.           wr->ddhandlers = p; /* get start of handler list */
  2314.  
  2315.           /*
  2316.            *  Handler strings are of the form:
  2317.            *  "<type> <type> ... <type> "
  2318.            */
  2319.           while (*p != '\0')
  2320.         {
  2321.           while ((*p != ' ') && (*p != '\0'))
  2322.             p++;
  2323.  
  2324.           *p++ = '\0';  /* null terminate handler type */
  2325.         }
  2326.         }
  2327.     }
  2328.  
  2329.       /*
  2330.        *  If this window has any children, then create DD_WinReps
  2331.        *  for them as well.
  2332.        */
  2333.       if (XQueryTree(dsPtr->display, wr->win, &root, &parent, &kids, &nkids))
  2334.     {
  2335.       wrtail = NULL;
  2336.       for (i=nkids-1; i >= 0; i--)
  2337.         {
  2338.           wrkid = WinRepAlloc(dsPtr->ddlist);
  2339.           wrkid->win = kids[i];
  2340.           wrkid->parent = wr;
  2341.  
  2342.           if (wrtail)
  2343.         wrtail->next = wrkid;
  2344.           else
  2345.         wr->kids = wrkid;
  2346.  
  2347.           wrtail = wrkid;
  2348.         }
  2349.       if (kids != NULL) {
  2350.         XFree((caddr_t)kids); /* done with list of kids */
  2351.       }
  2352.     }
  2353.     }
  2354.   wr->initialized = ~0;
  2355. }
  2356.  
  2357.  
  2358. /*
  2359.  * ------------------------------------------------------------------------
  2360.  *  AddDDProp()
  2361.  *
  2362.  *  Attaches a "DragDropInfo" property to the given target window.  This
  2363.  *  property allows the drag&drop mechanism to recognize the window as
  2364.  *  a valid target, and stores important information including the
  2365.  *  interpreter managing the target and the pathname for the target
  2366.  *  window.  Usually invoked when the target is first registered or
  2367.  *  first exposed (so that the X-window really exists).
  2368.  * ------------------------------------------------------------------------
  2369.  */
  2370. static void
  2371. AddDDProp(dtPtr)
  2372.      DD_Target* dtPtr;  /* drag&drop target window data */
  2373. {
  2374.   Tcl_Interp *interp = dtPtr->ddlist->interp;
  2375.  
  2376.   Atom ddProperty;
  2377.   char buffer[MAX_PROP_SIZE], *path, *info;
  2378.   DD_TargetHndl *thandl;
  2379.  
  2380.   if (dtPtr->tkwin != None)
  2381.     {
  2382. #ifdef STk_CODE
  2383.       static char command[] = { "(winfo 'name *root*)" };
  2384. #else
  2385.       static char command[] = { "winfo name ." };
  2386. #endif
  2387.  
  2388.       path = Tk_PathName(dtPtr->tkwin);
  2389. #ifdef STk_CODE
  2390.       if (Tcl_Eval(interp, command)==TCL_OK)
  2391.     sprintf(buffer, "%s}%s}", interp->result, path);
  2392.       else
  2393.     sprintf(buffer, "}%s}", path);
  2394. #else
  2395.       if (Tcl_Eval(interp, command)==TCL_OK)
  2396.     sprintf(buffer, "%s]%s]", interp->result, path);
  2397.       else
  2398.     sprintf(buffer, "]%s]", path);
  2399. #endif
  2400.  
  2401.       Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  2402.       for (thandl=dtPtr->handlers; thandl; thandl=thandl->next)
  2403.     Tcl_AppendResult(interp, thandl->dataType, " ", (char*)NULL);
  2404.  
  2405.       ddProperty = XInternAtom(dtPtr->display, DRAGDROP_PROPINFO, False);
  2406.       info = interp->result;
  2407.  
  2408.       XChangeProperty(dtPtr->display, Tk_WindowId(dtPtr->tkwin),
  2409.               ddProperty, XA_STRING, 8, PropModeReplace,
  2410.               (unsigned char*)info, strlen(info)+1);
  2411.     }
  2412. }
  2413.  
  2414. /*
  2415.  * ------------------------------------------------------------------------
  2416.  *  DDTokenEventProc()
  2417.  *
  2418.  *  Invoked by the Tk dispatcher to handle widget events.
  2419.  *  Manages redraws for the drag&drop token window.
  2420.  * ------------------------------------------------------------------------
  2421.  */
  2422. static void
  2423. DDTokenEventProc(clientData, eventPtr)
  2424.      ClientData clientData;     /* data associated with widget */
  2425.      XEvent *eventPtr;          /* information about event */
  2426. {
  2427.   register DD_Source *dsPtr = (DD_Source*)clientData;
  2428.  
  2429.   if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0))
  2430.     {
  2431.       if (dsPtr->tokenwin)
  2432.     {
  2433.       Tk_Window tkwin = dsPtr->tokenwin;
  2434.       int bd;
  2435.  
  2436.       bd = dsPtr->tokenBorderWidth;
  2437.  
  2438.       Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
  2439.                  dsPtr->tokenOutline,
  2440.                  0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  2441.                  0, TK_RELIEF_FLAT);
  2442.       Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin),
  2443.                  dsPtr->tokenBorder,
  2444.                  bd, bd, Tk_Width(tkwin)-2*bd, Tk_Height(tkwin)-2*bd,
  2445.                  bd, (dsPtr->overTargetWin) ? TK_RELIEF_RAISED : TK_RELIEF_FLAT);
  2446.     }
  2447.     }
  2448.   else if (eventPtr->type == DestroyNotify)
  2449.     dsPtr->tokenwin = NULL;
  2450. }
  2451.  
  2452. /*
  2453.  * ------------------------------------------------------------------------
  2454.  *  MoveDDToken()
  2455.  *
  2456.  *  Invoked during "drag" operations to move a token window to its
  2457.  *  current "drag" coordinate.
  2458.  * ------------------------------------------------------------------------
  2459.  */
  2460. static void
  2461. MoveDDToken(dsPtr)
  2462.      DD_Source *dsPtr;  /* drag&drop source window data */
  2463. {
  2464.   int x = dsPtr->tokenx;
  2465.   int y = dsPtr->tokeny;
  2466.   Tk_Window tokenwin = dsPtr->tokenwin;
  2467.  
  2468.   int max;
  2469.   int vx, vy, vw, vh;
  2470.  
  2471.   /*
  2472.    *  Adjust current location for virtual root windows.
  2473.    */
  2474.   Tk_GetVRootGeometry(dsPtr->tkwin, &vx, &vy, &vw, &vh);
  2475.   x += vx;
  2476.   y += vy;
  2477.  
  2478.   max = WidthOfScreen(Tk_Screen(dsPtr->tkwin)) - Tk_Width(tokenwin);
  2479.   switch (dsPtr->tokenAnchor)
  2480.     {
  2481.     case TK_ANCHOR_NW:
  2482.     case TK_ANCHOR_W:
  2483.     case TK_ANCHOR_SW:
  2484.       break;
  2485.  
  2486.     case TK_ANCHOR_N:
  2487.     case TK_ANCHOR_CENTER:
  2488.     case TK_ANCHOR_S:
  2489.       x -= Tk_Width(tokenwin)/2;
  2490.       break;
  2491.  
  2492.     case TK_ANCHOR_NE:
  2493.     case TK_ANCHOR_E:
  2494.     case TK_ANCHOR_SE:
  2495.       x -= Tk_Width(tokenwin);
  2496.       break;
  2497.     }
  2498.   if (x > max) {
  2499.     x = max;
  2500.   } else if (x < 0) {
  2501.     x = 0;
  2502.   }
  2503.   max = HeightOfScreen(Tk_Screen(dsPtr->tkwin)) - Tk_Height(tokenwin);
  2504.   switch (dsPtr->tokenAnchor)
  2505.     {
  2506.     case TK_ANCHOR_NW:
  2507.     case TK_ANCHOR_N:
  2508.     case TK_ANCHOR_NE:
  2509.       break;
  2510.  
  2511.     case TK_ANCHOR_W:
  2512.     case TK_ANCHOR_CENTER:
  2513.     case TK_ANCHOR_E:
  2514.       y -= Tk_Height(tokenwin)/2;
  2515.       break;
  2516.  
  2517.     case TK_ANCHOR_SW:
  2518.     case TK_ANCHOR_S:
  2519.     case TK_ANCHOR_SE:
  2520.       y -= Tk_Height(tokenwin);
  2521.       break;
  2522.     }
  2523.   if (y > max) {
  2524.     y = max;
  2525.   } else if (y < 0) {
  2526.     y = 0;
  2527.   }
  2528.   if ((x != Tk_X(tokenwin)) || (y != Tk_Y(tokenwin)))
  2529.     Tk_MoveWindow(tokenwin, x, y);
  2530. }
  2531.  
  2532. /*
  2533.  * ------------------------------------------------------------------------
  2534.  *  UpdateDDToken()
  2535.  *
  2536.  *  Invoked when the event loop is idle to determine whether or not
  2537.  *  the current drag&drop token position is over another drag&drop
  2538.  *  target.
  2539.  * ------------------------------------------------------------------------
  2540.  */
  2541. static void
  2542. UpdateDDToken(clientData)
  2543.      ClientData clientData;  /* widget data */
  2544. {
  2545.   register DD_Source *dsPtr = (DD_Source*)clientData;
  2546.   Tk_Window tkwin = dsPtr->tokenwin;
  2547.  
  2548.   int status, bd;
  2549.  
  2550.   status = (FindTargetWin(dsPtr, dsPtr->tokenx, dsPtr->tokeny) != NULL);
  2551.  
  2552.   bd = dsPtr->tokenBorderWidth;
  2553.   if (dsPtr->overTargetWin ^ status)
  2554.     {
  2555.       Tk_Fill3DRectangle(dsPtr->tkwin, Tk_WindowId(tkwin),
  2556.              dsPtr->tokenOutline,
  2557.              0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  2558.              0, TK_RELIEF_FLAT);
  2559.       Tk_Fill3DRectangle(dsPtr->tkwin, Tk_WindowId(tkwin),
  2560.              dsPtr->tokenBorder,
  2561.              bd, bd, Tk_Width(tkwin)-2*bd, Tk_Height(tkwin)-2*bd,
  2562.              bd, (status) ? TK_RELIEF_RAISED : TK_RELIEF_FLAT);
  2563.  
  2564.       /*
  2565.        *  If the source has a site command, then invoke it to
  2566.        *  modify the appearance of the token window.  Pass any
  2567.        *  errors onto the drag&drop error handler.
  2568.        */
  2569.       if (dsPtr->sitecmd)
  2570.     {
  2571.       char buffer[200];
  2572.  
  2573.       sprintf(buffer, "%d", status);
  2574.       if ((Tcl_VarEval(dsPtr->ddlist->interp,
  2575. #ifdef STk_CODE
  2576.                "(", dsPtr->sitecmd," ",buffer," '",Tk_PathName(dsPtr->tokenwin), ")",
  2577. #else
  2578.                dsPtr->sitecmd," ",buffer," ",Tk_PathName(dsPtr->tokenwin),
  2579. #endif
  2580.                (char*)NULL) != TCL_OK) &&
  2581.           dsPtr->ddlist->errorProc && *dsPtr->ddlist->errorProc)
  2582.  
  2583.         (void) Tcl_VarEval(dsPtr->ddlist->interp,
  2584. #ifdef STk_CODE
  2585.                    "(", dsPtr->ddlist->errorProc, " ",
  2586.                    (char *) STk_stringify(dsPtr->ddlist->interp->result, 0), ")",
  2587. #else
  2588.  
  2589.                    dsPtr->ddlist->errorProc, " {",
  2590.                    dsPtr->ddlist->interp->result, "}",
  2591. #endif
  2592.                    (char*)NULL);
  2593.     }
  2594.     }
  2595.   dsPtr->overTargetWin = status;
  2596. }
  2597.  
  2598. /*
  2599.  * ------------------------------------------------------------------------
  2600.  *  HideDDToken()
  2601.  *
  2602.  *  Unmaps the drag&drop token.  Invoked directly at the end of a
  2603.  *  successful communication, or after a delay if the communication
  2604.  *  fails (allowing the user to see a graphical picture of failure).
  2605.  * ------------------------------------------------------------------------
  2606.  */
  2607. static void
  2608. HideDDToken(clientData)
  2609.      ClientData clientData;  /* widget data */
  2610. {
  2611.   register DD_Source *dsPtr = (DD_Source*)clientData;
  2612.  
  2613.   if (dsPtr->tokenwin)
  2614.     Tk_UnmapWindow(dsPtr->tokenwin);
  2615.  
  2616.   dsPtr->hidetoken = NULL;
  2617. }
  2618.  
  2619. /*
  2620.  * ------------------------------------------------------------------------
  2621.  *  RejectDDToken()
  2622.  *
  2623.  *  Draws a rejection mark on the current drag&drop token, and arranges
  2624.  *  for the token to be unmapped after a small delay.
  2625.  * ------------------------------------------------------------------------
  2626.  */
  2627. static void
  2628. RejectDDToken(dsPtr)
  2629.      DD_Source* dsPtr;  /* widget data */
  2630. {
  2631.   int div = 6;            /* controls size of rejection symbol */
  2632.   int w,h,lwid,x,y,margin;
  2633.  
  2634.   Tk_Window tkwin = dsPtr->tokenwin;
  2635.  
  2636.   margin = 2*dsPtr->tokenBorderWidth;
  2637.   w = Tk_Width(tkwin) - 2*margin;
  2638.   h = Tk_Height(tkwin) - 2*margin;
  2639.  
  2640.   lwid = (w < h) ? w/div : h/div;
  2641.   lwid = (lwid < 1) ? 1 : lwid;
  2642.  
  2643.   w = h = lwid*(div-1);
  2644.   x = (Tk_Width(tkwin) - w)/2;
  2645.   y = (Tk_Height(tkwin) - h)/2;
  2646.  
  2647.   /*
  2648.    *  Draw the rejection symbol background (\) on the token window...
  2649.    */
  2650.   XSetLineAttributes(Tk_Display(tkwin), dsPtr->rejectBgGC,
  2651.              lwid+4, LineSolid, CapButt, JoinBevel);
  2652.  
  2653.   XDrawArc(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectBgGC,
  2654.        x, y, w, h, 0, 23040);
  2655.  
  2656.   XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectBgGC,
  2657.         x+lwid, y+lwid, x+w-lwid, y+h-lwid);
  2658.  
  2659.   /*
  2660.    *  Draw the rejection symbol foreground (\) on the token window...
  2661.    */
  2662.   XSetLineAttributes(Tk_Display(tkwin), dsPtr->rejectFgGC,
  2663.              lwid, LineSolid, CapButt, JoinBevel);
  2664.  
  2665.   XDrawArc(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectFgGC,
  2666.        x, y, w, h, 0, 23040);
  2667.  
  2668.   XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectFgGC,
  2669.         x+lwid, y+lwid, x+w-lwid, y+h-lwid);
  2670.  
  2671.   /*
  2672.    *  Arrange for token window to disappear eventually.
  2673.    */
  2674.   dsPtr->hidetoken
  2675.     = Tk_CreateTimerHandler(1000, HideDDToken, (ClientData)dsPtr);
  2676. }
  2677.  
  2678.  
  2679. /*
  2680.  * ------------------------------------------------------------------------
  2681.  *  StackInit()
  2682.  *
  2683.  *  Initializes a stack structure, allocating a certain amount of memory
  2684.  *  for the stack and setting the stack length to zero.
  2685.  * ------------------------------------------------------------------------
  2686.  */
  2687. static void
  2688. StackInit(stack)
  2689.      DD_Stack *stack;     /* stack to be initialized */
  2690. {
  2691.   stack->values = stack->space;
  2692.   stack->max = sizeof(stack->space)/sizeof(ClientData);
  2693.   stack->len = 0;
  2694. }
  2695.  
  2696. /*
  2697.  * ------------------------------------------------------------------------
  2698.  *  StackDelete()
  2699.  *
  2700.  *  Destroys a stack structure, freeing any memory that may have been
  2701.  *  allocated to represent it.
  2702.  * ------------------------------------------------------------------------
  2703.  */
  2704. static void
  2705. StackDelete(stack)
  2706.      DD_Stack *stack;     /* stack to be deleted */
  2707. {
  2708.   if (stack->values != stack->space) /* allocated extra memory? */
  2709.     free((char*)stack->values);    /* then free it */
  2710.  
  2711.   stack->values = NULL;
  2712.   stack->len = stack->max = 0;
  2713. }
  2714.  
  2715. /*
  2716.  * ------------------------------------------------------------------------
  2717.  *  StackPush()
  2718.  *
  2719.  *  Pushes a piece of client data onto the top of the given stack.
  2720.  * ------------------------------------------------------------------------
  2721.  */
  2722. static void
  2723. StackPush(cdata,stack)
  2724.      ClientData cdata;    /* data to be pushed onto stack */
  2725.      DD_Stack *stack;     /* stack */
  2726. {
  2727.   ClientData *newStack;
  2728.  
  2729.   if (stack->len+1 >= stack->max)
  2730.     {
  2731.       stack->max = (stack->max == 0) ? 5 : 2*stack->max;
  2732.       newStack = (ClientData*)
  2733.     malloc((unsigned)(stack->max*sizeof(ClientData)));
  2734.  
  2735.       if (stack->values)
  2736.     {
  2737.       memcpy((char *)newStack, (char *)stack->values,
  2738.          stack->len*sizeof(ClientData));
  2739.  
  2740.       if (stack->values != stack->space)
  2741.         free((char*)stack->values);
  2742.     }
  2743.       stack->values = newStack;
  2744.     }
  2745.   stack->values[stack->len++] = cdata;
  2746. }
  2747.  
  2748. /*
  2749.  * ------------------------------------------------------------------------
  2750.  *  StackPop()
  2751.  *
  2752.  *  Pops a bit of client data from the top of the given stack.
  2753.  * ------------------------------------------------------------------------
  2754.  */
  2755. static ClientData
  2756. StackPop(stack)
  2757.      DD_Stack *stack;  /* stack to be manipulated */
  2758. {
  2759.   if ((stack->values != NULL) && (stack->len > 0)) {
  2760.     return (stack->values[--stack->len]);
  2761.   }
  2762.   return (ClientData) 0;
  2763. }
  2764.